summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-01-19 10:01:25 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-01-19 10:01:25 +0000
commitd8e91e46262bc44006913e6796843909f1ac7bcd (patch)
tree7d0c143d9b38190e0fa0180805389da22cd834c5 /tools
parentb7eb8e35e481a74962664b63dfb09483b200209a (diff)
Notes
Diffstat (limited to 'tools')
-rw-r--r--tools/CMakeLists.txt2
-rw-r--r--tools/LLVMBuild.txt1
-rw-r--r--tools/bugpoint-passes/CMakeLists.txt2
-rw-r--r--tools/bugpoint-passes/TestPasses.cpp25
-rw-r--r--tools/bugpoint/CrashDebugger.cpp100
-rw-r--r--tools/bugpoint/ExecutionDriver.cpp39
-rw-r--r--tools/bugpoint/OptimizerDriver.cpp4
-rw-r--r--tools/bugpoint/ToolRunner.cpp92
-rw-r--r--tools/bugpoint/ToolRunner.h14
-rw-r--r--tools/dsymutil/CFBundle.h5
-rw-r--r--tools/dsymutil/CMakeLists.txt1
-rw-r--r--tools/dsymutil/CompileUnit.cpp6
-rw-r--r--tools/dsymutil/CompileUnit.h6
-rw-r--r--tools/dsymutil/DebugMap.cpp6
-rw-r--r--tools/dsymutil/DebugMap.h12
-rw-r--r--tools/dsymutil/DeclContext.h6
-rw-r--r--tools/dsymutil/DwarfLinker.cpp386
-rw-r--r--tools/dsymutil/DwarfLinker.h10
-rw-r--r--tools/dsymutil/DwarfStreamer.cpp99
-rw-r--r--tools/dsymutil/DwarfStreamer.h13
-rw-r--r--tools/dsymutil/LinkUtils.h6
-rw-r--r--tools/dsymutil/MachODebugMapParser.cpp23
-rw-r--r--tools/dsymutil/MachOUtils.cpp59
-rw-r--r--tools/dsymutil/MachOUtils.h7
-rw-r--r--tools/dsymutil/NonRelocatableStringpool.cpp26
-rw-r--r--tools/dsymutil/NonRelocatableStringpool.h11
-rw-r--r--tools/dsymutil/SymbolMap.cpp162
-rw-r--r--tools/dsymutil/SymbolMap.h54
-rw-r--r--tools/dsymutil/dsymutil.cpp100
-rw-r--r--tools/gold/CMakeLists.txt2
-rw-r--r--tools/gold/gold-plugin.cpp32
-rw-r--r--tools/lli/lli.cpp270
-rw-r--r--tools/llvm-ar/CMakeLists.txt1
-rw-r--r--tools/llvm-ar/llvm-ar.cpp319
-rw-r--r--tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp7
-rw-r--r--tools/llvm-c-test/debuginfo.c22
-rw-r--r--tools/llvm-c-test/echo.cpp134
-rw-r--r--tools/llvm-cfi-verify/lib/FileAnalysis.cpp56
-rw-r--r--tools/llvm-cfi-verify/lib/FileAnalysis.h12
-rw-r--r--tools/llvm-cfi-verify/lib/GraphBuilder.cpp18
-rw-r--r--tools/llvm-config/CMakeLists.txt14
-rw-r--r--tools/llvm-config/llvm-config.cpp2
-rw-r--r--tools/llvm-cov/CMakeLists.txt1
-rw-r--r--tools/llvm-cov/CodeCoverage.cpp60
-rw-r--r--tools/llvm-cov/CoverageExporter.h6
-rw-r--r--tools/llvm-cov/CoverageExporterJson.cpp458
-rw-r--r--tools/llvm-cov/CoverageExporterJson.h84
-rw-r--r--tools/llvm-cov/CoverageExporterLcov.cpp125
-rw-r--r--tools/llvm-cov/CoverageExporterLcov.h36
-rw-r--r--tools/llvm-cov/CoverageViewOptions.h3
-rw-r--r--tools/llvm-cov/SourceCoverageView.cpp10
-rw-r--r--tools/llvm-cov/SourceCoverageViewHTML.cpp2
-rw-r--r--tools/llvm-cov/TestingSupport.cpp2
-rw-r--r--tools/llvm-cvtres/llvm-cvtres.cpp2
-rw-r--r--tools/llvm-cxxdump/llvm-cxxdump.cpp18
-rw-r--r--tools/llvm-cxxmap/CMakeLists.txt8
-rw-r--r--tools/llvm-cxxmap/LLVMBuild.txt22
-rw-r--r--tools/llvm-cxxmap/llvm-cxxmap.cpp155
-rw-r--r--tools/llvm-diff/DifferenceEngine.cpp32
-rw-r--r--tools/llvm-dwarfdump/Statistics.cpp117
-rw-r--r--tools/llvm-dwarfdump/llvm-dwarfdump.cpp75
-rw-r--r--tools/llvm-dwp/llvm-dwp.cpp36
-rw-r--r--tools/llvm-elfabi/CMakeLists.txt11
-rw-r--r--tools/llvm-elfabi/ELFObjHandler.cpp68
-rw-r--r--tools/llvm-elfabi/ELFObjHandler.h33
-rw-r--r--tools/llvm-elfabi/ErrorCollector.cpp70
-rw-r--r--tools/llvm-elfabi/ErrorCollector.h75
-rw-r--r--tools/llvm-elfabi/LLVMBuild.txt22
-rw-r--r--tools/llvm-elfabi/llvm-elfabi.cpp143
-rw-r--r--tools/llvm-exegesis/CMakeLists.txt2
-rw-r--r--tools/llvm-exegesis/lib/AArch64/Target.cpp49
-rw-r--r--tools/llvm-exegesis/lib/Analysis.cpp246
-rw-r--r--tools/llvm-exegesis/lib/Analysis.h36
-rw-r--r--tools/llvm-exegesis/lib/Assembler.cpp72
-rw-r--r--tools/llvm-exegesis/lib/Assembler.h14
-rw-r--r--tools/llvm-exegesis/lib/BenchmarkCode.h41
-rw-r--r--tools/llvm-exegesis/lib/BenchmarkResult.cpp390
-rw-r--r--tools/llvm-exegesis/lib/BenchmarkResult.h66
-rw-r--r--tools/llvm-exegesis/lib/BenchmarkRunner.cpp237
-rw-r--r--tools/llvm-exegesis/lib/BenchmarkRunner.h86
-rw-r--r--tools/llvm-exegesis/lib/CMakeLists.txt15
-rw-r--r--tools/llvm-exegesis/lib/Clustering.cpp36
-rw-r--r--tools/llvm-exegesis/lib/Clustering.h13
-rw-r--r--tools/llvm-exegesis/lib/CodeTemplate.cpp119
-rw-r--r--tools/llvm-exegesis/lib/CodeTemplate.h131
-rw-r--r--tools/llvm-exegesis/lib/Latency.cpp234
-rw-r--r--tools/llvm-exegesis/lib/Latency.h28
-rw-r--r--tools/llvm-exegesis/lib/LlvmState.cpp21
-rw-r--r--tools/llvm-exegesis/lib/LlvmState.h19
-rw-r--r--tools/llvm-exegesis/lib/MCInstrDescView.cpp351
-rw-r--r--tools/llvm-exegesis/lib/MCInstrDescView.h158
-rw-r--r--tools/llvm-exegesis/lib/PerfHelper.cpp2
-rw-r--r--tools/llvm-exegesis/lib/PerfHelper.h2
-rw-r--r--tools/llvm-exegesis/lib/PowerPC/CMakeLists.txt18
-rw-r--r--tools/llvm-exegesis/lib/PowerPC/LLVMBuild.txt22
-rw-r--r--tools/llvm-exegesis/lib/PowerPC/Target.cpp76
-rw-r--r--tools/llvm-exegesis/lib/RegisterAliasing.cpp2
-rw-r--r--tools/llvm-exegesis/lib/RegisterAliasing.h3
-rw-r--r--tools/llvm-exegesis/lib/RegisterValue.cpp51
-rw-r--r--tools/llvm-exegesis/lib/RegisterValue.h53
-rw-r--r--tools/llvm-exegesis/lib/SnippetGenerator.cpp226
-rw-r--r--tools/llvm-exegesis/lib/SnippetGenerator.h98
-rw-r--r--tools/llvm-exegesis/lib/Target.cpp69
-rw-r--r--tools/llvm-exegesis/lib/Target.h79
-rw-r--r--tools/llvm-exegesis/lib/Uops.cpp219
-rw-r--r--tools/llvm-exegesis/lib/Uops.h52
-rw-r--r--tools/llvm-exegesis/lib/X86/Target.cpp674
-rw-r--r--tools/llvm-exegesis/llvm-exegesis.cpp380
-rw-r--r--tools/llvm-go/llvm-go.go1
-rw-r--r--tools/llvm-itanium-demangle-fuzzer/CMakeLists.txt (renamed from tools/llvm-demangle-fuzzer/CMakeLists.txt)4
-rw-r--r--tools/llvm-itanium-demangle-fuzzer/DummyDemanglerFuzzer.cpp (renamed from tools/llvm-demangle-fuzzer/DummyDemanglerFuzzer.cpp)0
-rw-r--r--tools/llvm-itanium-demangle-fuzzer/llvm-itanium-demangle-fuzzer.cpp (renamed from tools/llvm-demangle-fuzzer/llvm-demangle-fuzzer.cpp)0
-rw-r--r--tools/llvm-lto/llvm-lto.cpp18
-rw-r--r--tools/llvm-lto2/llvm-lto2.cpp2
-rw-r--r--tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp7
-rw-r--r--tools/llvm-mc/llvm-mc.cpp6
-rw-r--r--tools/llvm-mca/CMakeLists.txt43
-rw-r--r--tools/llvm-mca/CodeRegion.cpp21
-rw-r--r--tools/llvm-mca/CodeRegion.h27
-rw-r--r--tools/llvm-mca/CodeRegionGenerator.cpp137
-rw-r--r--tools/llvm-mca/CodeRegionGenerator.h70
-rw-r--r--tools/llvm-mca/Context.cpp63
-rw-r--r--tools/llvm-mca/Context.h68
-rw-r--r--tools/llvm-mca/DispatchStage.cpp153
-rw-r--r--tools/llvm-mca/DispatchStage.h106
-rw-r--r--tools/llvm-mca/DispatchStatistics.cpp71
-rw-r--r--tools/llvm-mca/ExecuteStage.cpp210
-rw-r--r--tools/llvm-mca/ExecuteStage.h67
-rw-r--r--tools/llvm-mca/FetchStage.cpp46
-rw-r--r--tools/llvm-mca/FetchStage.h45
-rw-r--r--tools/llvm-mca/HWEventListener.h141
-rw-r--r--tools/llvm-mca/HardwareUnit.cpp23
-rw-r--r--tools/llvm-mca/HardwareUnit.h31
-rw-r--r--tools/llvm-mca/InstrBuilder.cpp469
-rw-r--r--tools/llvm-mca/InstrBuilder.h85
-rw-r--r--tools/llvm-mca/Instruction.cpp177
-rw-r--r--tools/llvm-mca/Instruction.h434
-rw-r--r--tools/llvm-mca/InstructionTables.cpp70
-rw-r--r--tools/llvm-mca/InstructionTables.h43
-rw-r--r--tools/llvm-mca/LLVMBuild.txt2
-rw-r--r--tools/llvm-mca/LSUnit.cpp148
-rw-r--r--tools/llvm-mca/LSUnit.h147
-rw-r--r--tools/llvm-mca/Pipeline.cpp99
-rw-r--r--tools/llvm-mca/Pipeline.h79
-rw-r--r--tools/llvm-mca/PipelinePrinter.cpp6
-rw-r--r--tools/llvm-mca/PipelinePrinter.h6
-rw-r--r--tools/llvm-mca/RegisterFile.cpp343
-rw-r--r--tools/llvm-mca/RegisterFile.h172
-rw-r--r--tools/llvm-mca/RegisterFileStatistics.cpp107
-rw-r--r--tools/llvm-mca/RetireControlUnit.cpp87
-rw-r--r--tools/llvm-mca/RetireControlUnit.h98
-rw-r--r--tools/llvm-mca/RetireControlUnitStatistics.cpp49
-rw-r--r--tools/llvm-mca/RetireStage.cpp57
-rw-r--r--tools/llvm-mca/RetireStage.h48
-rw-r--r--tools/llvm-mca/Scheduler.cpp403
-rw-r--r--tools/llvm-mca/Scheduler.h515
-rw-r--r--tools/llvm-mca/SchedulerStatistics.cpp94
-rw-r--r--tools/llvm-mca/SourceMgr.h63
-rw-r--r--tools/llvm-mca/Stage.cpp27
-rw-r--r--tools/llvm-mca/Stage.h76
-rw-r--r--tools/llvm-mca/Support.cpp79
-rw-r--r--tools/llvm-mca/Support.h58
-rw-r--r--tools/llvm-mca/TimelineView.cpp240
-rw-r--r--tools/llvm-mca/Views/DispatchStatistics.cpp86
-rw-r--r--tools/llvm-mca/Views/DispatchStatistics.h (renamed from tools/llvm-mca/DispatchStatistics.h)6
-rw-r--r--tools/llvm-mca/Views/InstructionInfoView.cpp (renamed from tools/llvm-mca/InstructionInfoView.cpp)10
-rw-r--r--tools/llvm-mca/Views/InstructionInfoView.h (renamed from tools/llvm-mca/InstructionInfoView.h)13
-rw-r--r--tools/llvm-mca/Views/RegisterFileStatistics.cpp168
-rw-r--r--tools/llvm-mca/Views/RegisterFileStatistics.h (renamed from tools/llvm-mca/RegisterFileStatistics.h)28
-rw-r--r--tools/llvm-mca/Views/ResourcePressureView.cpp (renamed from tools/llvm-mca/ResourcePressureView.cpp)38
-rw-r--r--tools/llvm-mca/Views/ResourcePressureView.h (renamed from tools/llvm-mca/ResourcePressureView.h)33
-rw-r--r--tools/llvm-mca/Views/RetireControlUnitStatistics.cpp91
-rw-r--r--tools/llvm-mca/Views/RetireControlUnitStatistics.h (renamed from tools/llvm-mca/RetireControlUnitStatistics.h)35
-rw-r--r--tools/llvm-mca/Views/SchedulerStatistics.cpp183
-rw-r--r--tools/llvm-mca/Views/SchedulerStatistics.h (renamed from tools/llvm-mca/SchedulerStatistics.h)61
-rw-r--r--tools/llvm-mca/Views/SummaryView.cpp (renamed from tools/llvm-mca/SummaryView.cpp)40
-rw-r--r--tools/llvm-mca/Views/SummaryView.h (renamed from tools/llvm-mca/SummaryView.h)11
-rw-r--r--tools/llvm-mca/Views/TimelineView.cpp294
-rw-r--r--tools/llvm-mca/Views/TimelineView.h (renamed from tools/llvm-mca/TimelineView.h)34
-rw-r--r--tools/llvm-mca/Views/View.cpp (renamed from tools/llvm-mca/View.cpp)4
-rw-r--r--tools/llvm-mca/Views/View.h (renamed from tools/llvm-mca/View.h)4
-rw-r--r--tools/llvm-mca/llvm-mca.cpp252
-rw-r--r--tools/llvm-microsoft-demangle-fuzzer/CMakeLists.txt10
-rw-r--r--tools/llvm-microsoft-demangle-fuzzer/DummyDemanglerFuzzer.cpp19
-rw-r--r--tools/llvm-microsoft-demangle-fuzzer/llvm-microsoft-demangle-fuzzer.cpp21
-rw-r--r--tools/llvm-mt/Opts.td1
-rw-r--r--tools/llvm-mt/llvm-mt.cpp2
-rw-r--r--tools/llvm-nm/llvm-nm.cpp103
-rw-r--r--tools/llvm-objcopy/Buffer.cpp51
-rw-r--r--tools/llvm-objcopy/Buffer.h66
-rw-r--r--tools/llvm-objcopy/CMakeLists.txt9
-rw-r--r--tools/llvm-objcopy/COFF/COFFObjcopy.cpp98
-rw-r--r--tools/llvm-objcopy/COFF/COFFObjcopy.h31
-rw-r--r--tools/llvm-objcopy/COFF/Object.cpp70
-rw-r--r--tools/llvm-objcopy/COFF/Object.h148
-rw-r--r--tools/llvm-objcopy/COFF/Reader.cpp171
-rw-r--r--tools/llvm-objcopy/COFF/Reader.h43
-rw-r--r--tools/llvm-objcopy/COFF/Writer.cpp337
-rw-r--r--tools/llvm-objcopy/COFF/Writer.h61
-rw-r--r--tools/llvm-objcopy/CopyConfig.cpp474
-rw-r--r--tools/llvm-objcopy/CopyConfig.h119
-rw-r--r--tools/llvm-objcopy/ELF/ELFObjcopy.cpp584
-rw-r--r--tools/llvm-objcopy/ELF/ELFObjcopy.h34
-rw-r--r--tools/llvm-objcopy/ELF/Object.cpp (renamed from tools/llvm-objcopy/Object.cpp)514
-rw-r--r--tools/llvm-objcopy/ELF/Object.h (renamed from tools/llvm-objcopy/Object.h)231
-rw-r--r--tools/llvm-objcopy/ObjcopyOpts.td243
-rw-r--r--tools/llvm-objcopy/StripOpts.td86
-rw-r--r--tools/llvm-objcopy/llvm-objcopy.cpp682
-rw-r--r--tools/llvm-objcopy/llvm-objcopy.h2
-rw-r--r--tools/llvm-objdump/COFFDump.cpp29
-rw-r--r--tools/llvm-objdump/ELFDump.cpp30
-rw-r--r--tools/llvm-objdump/MachODump.cpp458
-rw-r--r--tools/llvm-objdump/llvm-objdump.cpp786
-rw-r--r--tools/llvm-objdump/llvm-objdump.h58
-rw-r--r--tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp11
-rw-r--r--tools/llvm-opt-report/CMakeLists.txt2
-rw-r--r--tools/llvm-opt-report/OptReport.cpp153
-rw-r--r--tools/llvm-pdbutil/Analyze.cpp148
-rw-r--r--tools/llvm-pdbutil/Analyze.h30
-rw-r--r--tools/llvm-pdbutil/CMakeLists.txt1
-rw-r--r--tools/llvm-pdbutil/DumpOutputStyle.cpp339
-rw-r--r--tools/llvm-pdbutil/DumpOutputStyle.h6
-rw-r--r--tools/llvm-pdbutil/InputFile.cpp17
-rw-r--r--tools/llvm-pdbutil/InputFile.h2
-rw-r--r--tools/llvm-pdbutil/MinimalSymbolDumper.cpp30
-rw-r--r--tools/llvm-pdbutil/MinimalSymbolDumper.h7
-rw-r--r--tools/llvm-pdbutil/MinimalTypeDumper.cpp38
-rw-r--r--tools/llvm-pdbutil/MinimalTypeDumper.h9
-rw-r--r--tools/llvm-pdbutil/PdbYaml.cpp6
-rw-r--r--tools/llvm-pdbutil/PdbYaml.h6
-rw-r--r--tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp7
-rw-r--r--tools/llvm-pdbutil/PrettyCompilandDumper.cpp11
-rw-r--r--tools/llvm-pdbutil/PrettyCompilandDumper.h1
-rw-r--r--tools/llvm-pdbutil/PrettyEnumDumper.cpp12
-rw-r--r--tools/llvm-pdbutil/PrettyFunctionDumper.cpp12
-rw-r--r--tools/llvm-pdbutil/PrettyTypeDumper.cpp178
-rw-r--r--tools/llvm-pdbutil/PrettyTypeDumper.h6
-rw-r--r--tools/llvm-pdbutil/PrettyTypedefDumper.cpp6
-rw-r--r--tools/llvm-pdbutil/YAMLOutputStyle.cpp45
-rw-r--r--tools/llvm-pdbutil/YAMLOutputStyle.h1
-rw-r--r--tools/llvm-pdbutil/llvm-pdbutil.cpp191
-rw-r--r--tools/llvm-pdbutil/llvm-pdbutil.h8
-rw-r--r--tools/llvm-profdata/llvm-profdata.cpp182
-rw-r--r--tools/llvm-rc/Opts.td2
-rw-r--r--tools/llvm-rc/ResourceFileWriter.cpp24
-rw-r--r--tools/llvm-rc/ResourceFileWriter.h2
-rw-r--r--tools/llvm-rc/ResourceScriptParser.cpp48
-rw-r--r--tools/llvm-rc/ResourceScriptParser.h5
-rw-r--r--tools/llvm-rc/ResourceScriptStmt.cpp6
-rw-r--r--tools/llvm-rc/ResourceScriptStmt.h71
-rw-r--r--tools/llvm-rc/ResourceVisitor.h2
-rw-r--r--tools/llvm-rc/llvm-rc.cpp10
-rw-r--r--tools/llvm-readobj/ARMWinEHPrinter.cpp445
-rw-r--r--tools/llvm-readobj/ARMWinEHPrinter.h51
-rw-r--r--tools/llvm-readobj/COFFDumper.cpp95
-rw-r--r--tools/llvm-readobj/DwarfCFIEHPrinter.h13
-rw-r--r--tools/llvm-readobj/ELFDumper.cpp667
-rw-r--r--tools/llvm-readobj/MachODumper.cpp10
-rw-r--r--tools/llvm-readobj/ObjDumper.h2
-rw-r--r--tools/llvm-readobj/WasmDumper.cpp41
-rw-r--r--tools/llvm-readobj/llvm-readobj.cpp212
-rw-r--r--tools/llvm-readobj/llvm-readobj.h11
-rw-r--r--tools/llvm-rtdyld/llvm-rtdyld.cpp25
-rw-r--r--tools/llvm-shlib/CMakeLists.txt160
-rw-r--r--tools/llvm-shlib/gen-msvc-exports.py106
-rw-r--r--tools/llvm-size/llvm-size.cpp60
-rw-r--r--tools/llvm-stress/llvm-stress.cpp4
-rw-r--r--tools/llvm-strings/llvm-strings.cpp12
-rw-r--r--tools/llvm-symbolizer/llvm-symbolizer.cpp110
-rw-r--r--tools/llvm-undname/llvm-undname.cpp13
-rw-r--r--tools/llvm-xray/CMakeLists.txt1
-rw-r--r--tools/llvm-xray/xray-account.cpp145
-rw-r--r--tools/llvm-xray/xray-converter.cpp130
-rw-r--r--tools/llvm-xray/xray-fdr-dump.cpp119
-rw-r--r--tools/llvm-xray/xray-graph.cpp4
-rw-r--r--tools/llvm-xray/xray-stacks.cpp5
-rw-r--r--tools/llvm-yaml-numeric-parser-fuzzer/CMakeLists.txt9
-rw-r--r--tools/llvm-yaml-numeric-parser-fuzzer/DummyYAMLNumericParserFuzzer.cpp19
-rw-r--r--tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp47
-rw-r--r--tools/lto/lto.cpp7
-rw-r--r--tools/lto/lto.exports2
-rw-r--r--tools/msbuild/.gitignore8
-rw-r--r--tools/msbuild/LLVM.Cpp.Common.props152
-rw-r--r--tools/msbuild/LLVM.Cpp.Common.targets315
-rw-r--r--tools/msbuild/llvm-general.xml40
-rw-r--r--tools/msbuild/llvm.csproj11
-rw-r--r--tools/msbuild/source.extension.vsixmanifest8
-rw-r--r--tools/obj2yaml/Error.cpp2
-rw-r--r--tools/obj2yaml/coff2yaml.cpp22
-rw-r--r--tools/obj2yaml/dwarf2yaml.cpp5
-rw-r--r--tools/obj2yaml/elf2yaml.cpp1
-rw-r--r--tools/obj2yaml/obj2yaml.cpp2
-rw-r--r--tools/obj2yaml/wasm2yaml.cpp54
-rw-r--r--tools/opt-remarks/CMakeLists.txt22
-rw-r--r--tools/opt-remarks/OptRemarks.exports6
-rw-r--r--tools/opt-remarks/liboptremarks.cpp (renamed from tools/llvm-mca/HWEventListener.cpp)19
-rwxr-xr-xtools/opt-viewer/opt-diff.py2
-rwxr-xr-xtools/opt-viewer/opt-stats.py2
-rwxr-xr-xtools/opt-viewer/opt-viewer.py7
-rw-r--r--tools/opt-viewer/optpmap.py2
-rw-r--r--tools/opt-viewer/optrecord.py2
-rw-r--r--tools/opt/Debugify.cpp11
-rw-r--r--tools/opt/NewPMDriver.cpp125
-rw-r--r--tools/opt/opt.cpp8
-rwxr-xr-xtools/sancov/coverage-report-server.py2
-rw-r--r--tools/sancov/sancov.cpp15
-rw-r--r--tools/sanstats/sanstats.cpp13
-rw-r--r--tools/xcode-toolchain/CMakeLists.txt2
-rw-r--r--tools/yaml2obj/yaml2coff.cpp14
-rw-r--r--tools/yaml2obj/yaml2elf.cpp43
-rw-r--r--tools/yaml2obj/yaml2macho.cpp7
-rw-r--r--tools/yaml2obj/yaml2wasm.cpp129
312 files changed, 14854 insertions, 11190 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index b654b8c5cb8e..ddafc98d63bb 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -17,7 +17,7 @@ else()
set(LLVM_TOOL_POLLY_BUILD Off)
endif()
-if(NOT LLVM_BUILD_LLVM_DYLIB )
+if(NOT LLVM_BUILD_LLVM_DYLIB AND NOT LLVM_BUILD_LLVM_C_DYLIB)
set(LLVM_TOOL_LLVM_SHLIB_BUILD Off)
endif()
diff --git a/tools/LLVMBuild.txt b/tools/LLVMBuild.txt
index 1732ea0cb8a0..61e053cac48a 100644
--- a/tools/LLVMBuild.txt
+++ b/tools/LLVMBuild.txt
@@ -32,6 +32,7 @@ subdirectories =
llvm-dis
llvm-dwarfdump
llvm-dwp
+ llvm-elfabi
llvm-exegesis
llvm-extract
llvm-jitlistener
diff --git a/tools/bugpoint-passes/CMakeLists.txt b/tools/bugpoint-passes/CMakeLists.txt
index e32b0a3aa34f..eea3e239b808 100644
--- a/tools/bugpoint-passes/CMakeLists.txt
+++ b/tools/bugpoint-passes/CMakeLists.txt
@@ -14,7 +14,7 @@ if(WIN32 OR CYGWIN)
set(LLVM_LINK_COMPONENTS Core)
endif()
-add_llvm_loadable_module( BugpointPasses
+add_llvm_library( BugpointPasses MODULE BUILDTREE_ONLY
TestPasses.cpp
DEPENDS
diff --git a/tools/bugpoint-passes/TestPasses.cpp b/tools/bugpoint-passes/TestPasses.cpp
index 22ded6261a1a..6b1463685b2c 100644
--- a/tools/bugpoint-passes/TestPasses.cpp
+++ b/tools/bugpoint-passes/TestPasses.cpp
@@ -123,3 +123,28 @@ char CrashOnTooManyCUs::ID = 0;
static RegisterPass<CrashOnTooManyCUs>
A("bugpoint-crash-too-many-cus",
"BugPoint Test Pass - Intentionally crash on too many CUs");
+
+namespace {
+class CrashOnFunctionAttribute : public FunctionPass {
+public:
+ static char ID; // Pass ID, replacement for typeid
+ CrashOnFunctionAttribute() : FunctionPass(ID) {}
+
+private:
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ }
+
+ bool runOnFunction(Function &F) override {
+ AttributeSet A = F.getAttributes().getFnAttributes();
+ if (A.hasAttribute("bugpoint-crash"))
+ abort();
+ return false;
+ }
+};
+} // namespace
+
+char CrashOnFunctionAttribute::ID = 0;
+static RegisterPass<CrashOnFunctionAttribute>
+ B("bugpoint-crashfuncattr", "BugPoint Test Pass - Intentionally crash on "
+ "function attribute 'bugpoint-crash'");
diff --git a/tools/bugpoint/CrashDebugger.cpp b/tools/bugpoint/CrashDebugger.cpp
index a5b31e1ab321..ef6a214fde20 100644
--- a/tools/bugpoint/CrashDebugger.cpp
+++ b/tools/bugpoint/CrashDebugger.cpp
@@ -315,6 +315,66 @@ bool ReduceCrashingFunctions::TestFuncs(std::vector<Function *> &Funcs) {
}
namespace {
+/// ReduceCrashingFunctionAttributes reducer - This works by removing
+/// attributes on a particular function and seeing if the program still crashes.
+/// If it does, then keep the newer, smaller program.
+///
+class ReduceCrashingFunctionAttributes : public ListReducer<Attribute> {
+ BugDriver &BD;
+ std::string FnName;
+ BugTester TestFn;
+
+public:
+ ReduceCrashingFunctionAttributes(BugDriver &bd, const std::string &FnName,
+ BugTester testFn)
+ : BD(bd), FnName(FnName), TestFn(testFn) {}
+
+ Expected<TestResult> doTest(std::vector<Attribute> &Prefix,
+ std::vector<Attribute> &Kept) override {
+ if (!Kept.empty() && TestFuncAttrs(Kept))
+ return KeepSuffix;
+ if (!Prefix.empty() && TestFuncAttrs(Prefix))
+ return KeepPrefix;
+ return NoFailure;
+ }
+
+ bool TestFuncAttrs(std::vector<Attribute> &Attrs);
+};
+}
+
+bool ReduceCrashingFunctionAttributes::TestFuncAttrs(
+ std::vector<Attribute> &Attrs) {
+ // Clone the program to try hacking it apart...
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram());
+ Function *F = M->getFunction(FnName);
+
+ // Build up an AttributeList from the attributes we've been given by the
+ // reducer.
+ AttrBuilder AB;
+ for (auto A : Attrs)
+ AB.addAttribute(A);
+ AttributeList NewAttrs;
+ NewAttrs =
+ NewAttrs.addAttributes(BD.getContext(), AttributeList::FunctionIndex, AB);
+
+ // Set this new list of attributes on the function.
+ F->setAttributes(NewAttrs);
+
+ // Try running on the hacked up program...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
+
+ // Pass along the set of attributes that caused the crash.
+ Attrs.clear();
+ for (Attribute A : NewAttrs.getFnAttributes()) {
+ Attrs.push_back(A);
+ }
+ return true;
+ }
+ return false;
+}
+
+namespace {
/// Simplify the CFG without completely destroying it.
/// This is not well defined, but basically comes down to "try to eliminate
/// unreachable blocks and constant fold terminators without deciding that
@@ -409,7 +469,7 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock *> &BBs) {
for (BasicBlock *Succ : successors(&BB))
Succ->removePredecessor(&BB);
- TerminatorInst *BBTerm = BB.getTerminator();
+ Instruction *BBTerm = BB.getTerminator();
if (BBTerm->isEHPad() || BBTerm->getType()->isTokenTy())
continue;
if (!BBTerm->getType()->isVoidTy())
@@ -703,7 +763,7 @@ bool ReduceCrashingInstructions::TestInsts(
// Convert list to set for fast lookup...
SmallPtrSet<Instruction *, 32> Instructions;
for (unsigned i = 0, e = Insts.size(); i != e; ++i) {
- assert(!isa<TerminatorInst>(Insts[i]));
+ assert(!Insts[i]->isTerminator());
Instructions.insert(cast<Instruction>(VMap[Insts[i]]));
}
@@ -717,7 +777,7 @@ bool ReduceCrashingInstructions::TestInsts(
for (Function::iterator FI = MI->begin(), FE = MI->end(); FI != FE; ++FI)
for (BasicBlock::iterator I = FI->begin(), E = FI->end(); I != E;) {
Instruction *Inst = &*I++;
- if (!Instructions.count(Inst) && !isa<TerminatorInst>(Inst) &&
+ if (!Instructions.count(Inst) && !Inst->isTerminator() &&
!Inst->isEHPad() && !Inst->getType()->isTokenTy() &&
!Inst->isSwiftError()) {
if (!Inst->getType()->isVoidTy())
@@ -950,7 +1010,7 @@ static Error ReduceInsts(BugDriver &BD, BugTester TestFn) {
for (const Function &F : BD.getProgram())
for (const BasicBlock &BB : F)
for (const Instruction &I : BB)
- if (!isa<TerminatorInst>(&I))
+ if (!I.isTerminator())
Insts.push_back(&I);
Expected<bool> Result =
@@ -1056,6 +1116,38 @@ static Error DebugACrash(BugDriver &BD, BugTester TestFn) {
BD.EmitProgressBitcode(BD.getProgram(), "reduced-function");
}
+ // For each remaining function, try to reduce that function's attributes.
+ std::vector<std::string> FunctionNames;
+ for (Function &F : BD.getProgram())
+ FunctionNames.push_back(F.getName());
+
+ if (!FunctionNames.empty() && !BugpointIsInterrupted) {
+ outs() << "\n*** Attempting to reduce the number of function attributes in "
+ "the testcase\n";
+
+ unsigned OldSize = 0;
+ unsigned NewSize = 0;
+ for (std::string &Name : FunctionNames) {
+ Function *Fn = BD.getProgram().getFunction(Name);
+ assert(Fn && "Could not find funcion?");
+
+ std::vector<Attribute> Attrs;
+ for (Attribute A : Fn->getAttributes().getFnAttributes())
+ Attrs.push_back(A);
+
+ OldSize += Attrs.size();
+ Expected<bool> Result =
+ ReduceCrashingFunctionAttributes(BD, Name, TestFn).reduceList(Attrs);
+ if (Error E = Result.takeError())
+ return E;
+
+ NewSize += Attrs.size();
+ }
+
+ if (OldSize < NewSize)
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-function-attributes");
+ }
+
// Attempt to change conditional branches into unconditional branches to
// eliminate blocks.
if (!DisableSimplifyCFG && !BugpointIsInterrupted) {
diff --git a/tools/bugpoint/ExecutionDriver.cpp b/tools/bugpoint/ExecutionDriver.cpp
index 773bad69fae0..1b86b103d835 100644
--- a/tools/bugpoint/ExecutionDriver.cpp
+++ b/tools/bugpoint/ExecutionDriver.cpp
@@ -148,8 +148,9 @@ Error BugDriver::initializeExecutionEnvironment() {
std::string Message;
if (CCBinary.empty()) {
- if (sys::findProgramByName("clang"))
- CCBinary = "clang";
+ if (ErrorOr<std::string> ClangPath =
+ FindProgramByName("clang", getToolName(), &AbsTolerance))
+ CCBinary = *ClangPath;
else
CCBinary = "gcc";
}
@@ -193,11 +194,11 @@ Error BugDriver::initializeExecutionEnvironment() {
break;
case CompileCustom:
Interpreter = AbstractInterpreter::createCustomCompiler(
- Message, CustomCompileCommand);
+ getToolName(), Message, CustomCompileCommand);
break;
case Custom:
- Interpreter =
- AbstractInterpreter::createCustomExecutor(Message, CustomExecCommand);
+ Interpreter = AbstractInterpreter::createCustomExecutor(
+ getToolName(), Message, CustomExecCommand);
break;
}
if (!Interpreter)
@@ -239,8 +240,8 @@ Error BugDriver::initializeExecutionEnvironment() {
SafeInterpreterSel == RunLLCIA);
break;
case Custom:
- SafeInterpreter =
- AbstractInterpreter::createCustomExecutor(Message, CustomExecCommand);
+ SafeInterpreter = AbstractInterpreter::createCustomExecutor(
+ getToolName(), Message, CustomExecCommand);
break;
default:
Message = "Sorry, this back-end is not supported by bugpoint as the "
@@ -252,7 +253,7 @@ Error BugDriver::initializeExecutionEnvironment() {
exit(1);
}
- cc = CC::create(Message, CCBinary, &CCToolArgv);
+ cc = CC::create(getToolName(), Message, CCBinary, &CCToolArgv);
if (!cc) {
outs() << Message << "\nExiting.\n";
exit(1);
@@ -299,26 +300,32 @@ Expected<std::string> BugDriver::executeProgram(const Module &Program,
if (!AI)
AI = Interpreter;
assert(AI && "Interpreter should have been created already!");
+ bool CreatedBitcode = false;
if (BitcodeFile.empty()) {
// Emit the program to a bitcode file...
- auto File =
- sys::fs::TempFile::create(OutputPrefix + "-test-program-%%%%%%%.bc");
- if (!File) {
- errs() << ToolName
- << ": Error making unique filename: " << toString(File.takeError())
+ SmallString<128> UniqueFilename;
+ int UniqueFD;
+ std::error_code EC = sys::fs::createUniqueFile(
+ OutputPrefix + "-test-program-%%%%%%%.bc", UniqueFD, UniqueFilename);
+ if (EC) {
+ errs() << ToolName << ": Error making unique filename: " << EC.message()
<< "!\n";
exit(1);
}
- DiscardTemp Discard{*File};
- BitcodeFile = File->TmpName;
+ BitcodeFile = UniqueFilename.str();
- if (writeProgramToFile(File->FD, Program)) {
+ if (writeProgramToFile(BitcodeFile, UniqueFD, Program)) {
errs() << ToolName << ": Error emitting bitcode to file '" << BitcodeFile
<< "'!\n";
exit(1);
}
+ CreatedBitcode = true;
}
+ // Remove the temporary bitcode file when we are done.
+ std::string BitcodePath(BitcodeFile);
+ FileRemover BitcodeFileRemover(BitcodePath, CreatedBitcode && !SaveTemps);
+
if (OutputFile.empty())
OutputFile = OutputPrefix + "-execution-output-%%%%%%%";
diff --git a/tools/bugpoint/OptimizerDriver.cpp b/tools/bugpoint/OptimizerDriver.cpp
index cbb048db8fe7..64fe675de20c 100644
--- a/tools/bugpoint/OptimizerDriver.cpp
+++ b/tools/bugpoint/OptimizerDriver.cpp
@@ -16,6 +16,7 @@
//===----------------------------------------------------------------------===//
#include "BugDriver.h"
+#include "ToolRunner.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Module.h"
@@ -166,7 +167,8 @@ bool BugDriver::runPasses(Module &Program,
std::string tool = OptCmd;
if (OptCmd.empty()) {
- if (ErrorOr<std::string> Path = sys::findProgramByName("opt"))
+ if (ErrorOr<std::string> Path =
+ FindProgramByName("opt", getToolName(), &OutputPrefix))
tool = *Path;
else
errs() << Path.getError().message() << "\n";
diff --git a/tools/bugpoint/ToolRunner.cpp b/tools/bugpoint/ToolRunner.cpp
index 812e8e3bbae5..7ba8ea1f16c5 100644
--- a/tools/bugpoint/ToolRunner.cpp
+++ b/tools/bugpoint/ToolRunner.cpp
@@ -202,19 +202,7 @@ Expected<int> LLI::ExecuteProgram(const std::string &Bitcode,
void AbstractInterpreter::anchor() {}
-#if defined(LLVM_ON_UNIX)
-const char EXESuffix[] = "";
-#elif defined(_WIN32)
-const char EXESuffix[] = "exe";
-#endif
-
-/// Prepend the path to the program being executed
-/// to \p ExeName, given the value of argv[0] and the address of main()
-/// itself. This allows us to find another LLVM tool if it is built in the same
-/// directory. An empty string is returned on error; note that this function
-/// just mainpulates the path and doesn't check for executability.
-/// Find a named executable.
-static std::string PrependMainExecutablePath(const std::string &ExeName,
+ErrorOr<std::string> llvm::FindProgramByName(const std::string &ExeName,
const char *Argv0,
void *MainAddr) {
// Check the directory that the calling program is in. We can do
@@ -222,30 +210,25 @@ static std::string PrependMainExecutablePath(const std::string &ExeName,
// is a relative path to the executable itself.
std::string Main = sys::fs::getMainExecutable(Argv0, MainAddr);
StringRef Result = sys::path::parent_path(Main);
+ if (ErrorOr<std::string> Path = sys::findProgramByName(ExeName, Result))
+ return *Path;
- if (!Result.empty()) {
- SmallString<128> Storage = Result;
- sys::path::append(Storage, ExeName);
- sys::path::replace_extension(Storage, EXESuffix);
- return Storage.str();
- }
-
- return Result.str();
+ // Check the user PATH.
+ return sys::findProgramByName(ExeName);
}
// LLI create method - Try to find the LLI executable
AbstractInterpreter *
AbstractInterpreter::createLLI(const char *Argv0, std::string &Message,
const std::vector<std::string> *ToolArgs) {
- std::string LLIPath =
- PrependMainExecutablePath("lli", Argv0, (void *)(intptr_t)&createLLI);
- if (!LLIPath.empty()) {
- Message = "Found lli: " + LLIPath + "\n";
- return new LLI(LLIPath, ToolArgs);
+ if (ErrorOr<std::string> LLIPath =
+ FindProgramByName("lli", Argv0, (void *)(intptr_t)&createLLI)) {
+ Message = "Found lli: " + *LLIPath + "\n";
+ return new LLI(*LLIPath, ToolArgs);
+ } else {
+ Message = LLIPath.getError().message() + "\n";
+ return nullptr;
}
-
- Message = "Cannot find `lli' in executable directory!\n";
- return nullptr;
}
//===---------------------------------------------------------------------===//
@@ -368,8 +351,9 @@ Expected<int> CustomExecutor::ExecuteProgram(
// '\ ' -> ' '
// 'exa\mple' -> 'example'
//
-static void lexCommand(std::string &Message, const std::string &CommandLine,
- std::string &CmdPath, std::vector<std::string> &Args) {
+static void lexCommand(const char *Argv0, std::string &Message,
+ const std::string &CommandLine, std::string &CmdPath,
+ std::vector<std::string> &Args) {
std::string Token;
std::string Command;
@@ -402,7 +386,7 @@ static void lexCommand(std::string &Message, const std::string &CommandLine,
Token.push_back(CommandLine[Pos]);
}
- auto Path = sys::findProgramByName(Command);
+ auto Path = FindProgramByName(Command, Argv0, (void *)(intptr_t)&lexCommand);
if (!Path) {
Message = std::string("Cannot find '") + Command +
"' in PATH: " + Path.getError().message() + "\n";
@@ -416,11 +400,12 @@ static void lexCommand(std::string &Message, const std::string &CommandLine,
// Custom execution environment create method, takes the execution command
// as arguments
AbstractInterpreter *AbstractInterpreter::createCustomCompiler(
- std::string &Message, const std::string &CompileCommandLine) {
+ const char *Argv0, std::string &Message,
+ const std::string &CompileCommandLine) {
std::string CmdPath;
std::vector<std::string> Args;
- lexCommand(Message, CompileCommandLine, CmdPath, Args);
+ lexCommand(Argv0, Message, CompileCommandLine, CmdPath, Args);
if (CmdPath.empty())
return nullptr;
@@ -430,12 +415,13 @@ AbstractInterpreter *AbstractInterpreter::createCustomCompiler(
// Custom execution environment create method, takes the execution command
// as arguments
AbstractInterpreter *
-AbstractInterpreter::createCustomExecutor(std::string &Message,
+AbstractInterpreter::createCustomExecutor(const char *Argv0,
+ std::string &Message,
const std::string &ExecCommandLine) {
std::string CmdPath;
std::vector<std::string> Args;
- lexCommand(Message, ExecCommandLine, CmdPath, Args);
+ lexCommand(Argv0, Message, ExecCommandLine, CmdPath, Args);
if (CmdPath.empty())
return nullptr;
@@ -524,20 +510,20 @@ LLC *AbstractInterpreter::createLLC(const char *Argv0, std::string &Message,
const std::vector<std::string> *Args,
const std::vector<std::string> *CCArgs,
bool UseIntegratedAssembler) {
- std::string LLCPath =
- PrependMainExecutablePath("llc", Argv0, (void *)(intptr_t)&createLLC);
- if (LLCPath.empty()) {
- Message = "Cannot find `llc' in executable directory!\n";
+ ErrorOr<std::string> LLCPath =
+ FindProgramByName("llc", Argv0, (void *)(intptr_t)&createLLC);
+ if (!LLCPath) {
+ Message = LLCPath.getError().message() + "\n";
return nullptr;
}
- CC *cc = CC::create(Message, CCBinary, CCArgs);
+ CC *cc = CC::create(Argv0, Message, CCBinary, CCArgs);
if (!cc) {
errs() << Message << "\n";
exit(1);
}
- Message = "Found llc: " + LLCPath + "\n";
- return new LLC(LLCPath, cc, Args, UseIntegratedAssembler);
+ Message = "Found llc: " + *LLCPath + "\n";
+ return new LLC(*LLCPath, cc, Args, UseIntegratedAssembler);
}
//===---------------------------------------------------------------------===//
@@ -606,15 +592,14 @@ Expected<int> JIT::ExecuteProgram(const std::string &Bitcode,
AbstractInterpreter *
AbstractInterpreter::createJIT(const char *Argv0, std::string &Message,
const std::vector<std::string> *Args) {
- std::string LLIPath =
- PrependMainExecutablePath("lli", Argv0, (void *)(intptr_t)&createJIT);
- if (!LLIPath.empty()) {
- Message = "Found lli: " + LLIPath + "\n";
- return new JIT(LLIPath, Args);
+ if (ErrorOr<std::string> LLIPath =
+ FindProgramByName("lli", Argv0, (void *)(intptr_t)&createJIT)) {
+ Message = "Found lli: " + *LLIPath + "\n";
+ return new JIT(*LLIPath, Args);
+ } else {
+ Message = LLIPath.getError().message() + "\n";
+ return nullptr;
}
-
- Message = "Cannot find `lli' in executable directory!\n";
- return nullptr;
}
//===---------------------------------------------------------------------===//
@@ -855,9 +840,10 @@ Error CC::MakeSharedObject(const std::string &InputFile, FileType fileType,
/// create - Try to find the CC executable
///
-CC *CC::create(std::string &Message, const std::string &CCBinary,
+CC *CC::create(const char *Argv0, std::string &Message,
+ const std::string &CCBinary,
const std::vector<std::string> *Args) {
- auto CCPath = sys::findProgramByName(CCBinary);
+ auto CCPath = FindProgramByName(CCBinary, Argv0, (void *)(intptr_t)&create);
if (!CCPath) {
Message = "Cannot find `" + CCBinary + "' in PATH: " +
CCPath.getError().message() + "\n";
diff --git a/tools/bugpoint/ToolRunner.h b/tools/bugpoint/ToolRunner.h
index f218ad534ee9..ef8551cc669b 100644
--- a/tools/bugpoint/ToolRunner.h
+++ b/tools/bugpoint/ToolRunner.h
@@ -49,7 +49,8 @@ class CC {
public:
enum FileType { AsmFile, ObjectFile, CFile };
- static CC *create(std::string &Message, const std::string &CCBinary,
+ static CC *create(const char *Argv0, std::string &Message,
+ const std::string &CCBinary,
const std::vector<std::string> *Args);
/// ExecuteProgram - Execute the program specified by "ProgramFile" (which is
@@ -98,11 +99,11 @@ public:
const std::vector<std::string> *Args = nullptr);
static AbstractInterpreter *
- createCustomCompiler(std::string &Message,
+ createCustomCompiler(const char *Argv0, std::string &Message,
const std::string &CompileCommandLine);
static AbstractInterpreter *
- createCustomExecutor(std::string &Message,
+ createCustomExecutor(const char *Argv0, std::string &Message,
const std::string &ExecCommandLine);
virtual ~AbstractInterpreter() {}
@@ -178,6 +179,13 @@ public:
unsigned MemoryLimit = 0) override;
};
+/// Find the first executable file \ExeName, either in the user's PATH or,
+/// failing that, in the same directory as argv[0]. This allows us to find
+/// another LLVM tool if it is built in the same directory. If no executable is
+/// found, an error is returned.
+ErrorOr<std::string> FindProgramByName(const std::string &ExeName,
+ const char *Argv0, void *MainAddr);
+
} // End llvm namespace
#endif
diff --git a/tools/dsymutil/CFBundle.h b/tools/dsymutil/CFBundle.h
index bdbecb4785c0..5f241849910d 100644
--- a/tools/dsymutil/CFBundle.h
+++ b/tools/dsymutil/CFBundle.h
@@ -7,6 +7,9 @@
//
//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_DSYMUTIL_CFBUNDLE_H
+#define LLVM_TOOLS_DSYMUTIL_CFBUNDLE_H
+
#include "llvm/ADT/StringRef.h"
#include <string>
@@ -24,3 +27,5 @@ CFBundleInfo getBundleInfo(llvm::StringRef ExePath);
} // end namespace dsymutil
} // end namespace llvm
+
+#endif
diff --git a/tools/dsymutil/CMakeLists.txt b/tools/dsymutil/CMakeLists.txt
index f41a6fd28504..480f78fb1888 100644
--- a/tools/dsymutil/CMakeLists.txt
+++ b/tools/dsymutil/CMakeLists.txt
@@ -20,6 +20,7 @@ add_llvm_tool(dsymutil
MachODebugMapParser.cpp
MachOUtils.cpp
NonRelocatableStringpool.cpp
+ SymbolMap.cpp
DEPENDS
intrinsics_gen
diff --git a/tools/dsymutil/CompileUnit.cpp b/tools/dsymutil/CompileUnit.cpp
index 67e1739ae108..4654e41b2176 100644
--- a/tools/dsymutil/CompileUnit.cpp
+++ b/tools/dsymutil/CompileUnit.cpp
@@ -92,7 +92,11 @@ void CompileUnit::addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset) {
void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc,
int64_t PcOffset) {
- Ranges.insert(FuncLowPc, FuncHighPc, PcOffset);
+ // Don't add empty ranges to the interval map. They are a problem because
+ // the interval map expects half open intervals. This is safe because they
+ // are empty anyway.
+ if (FuncHighPc != FuncLowPc)
+ Ranges.insert(FuncLowPc, FuncHighPc, PcOffset);
this->LowPc = std::min(LowPc, FuncLowPc + PcOffset);
this->HighPc = std::max(HighPc, FuncHighPc + PcOffset);
}
diff --git a/tools/dsymutil/CompileUnit.h b/tools/dsymutil/CompileUnit.h
index 0f5efdd68051..79b88fd4d6bb 100644
--- a/tools/dsymutil/CompileUnit.h
+++ b/tools/dsymutil/CompileUnit.h
@@ -7,13 +7,13 @@
//
//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H
+#define LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H
+
#include "llvm/ADT/IntervalMap.h"
#include "llvm/CodeGen/DIE.h"
#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
-#ifndef LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H
-#define LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H
-
namespace llvm {
namespace dsymutil {
diff --git a/tools/dsymutil/DebugMap.cpp b/tools/dsymutil/DebugMap.cpp
index 26137773a4e7..509afaf56f4e 100644
--- a/tools/dsymutil/DebugMap.cpp
+++ b/tools/dsymutil/DebugMap.cpp
@@ -63,9 +63,9 @@ void DebugMapObject::print(raw_ostream &OS) const {
Entries.reserve(Symbols.getNumItems());
for (const auto &Sym : make_range(Symbols.begin(), Symbols.end()))
Entries.push_back(std::make_pair(Sym.getKey(), Sym.getValue()));
- llvm::sort(
- Entries.begin(), Entries.end(),
- [](const Entry &LHS, const Entry &RHS) { return LHS.first < RHS.first; });
+ llvm::sort(Entries, [](const Entry &LHS, const Entry &RHS) {
+ return LHS.first < RHS.first;
+ });
for (const auto &Sym : Entries) {
if (Sym.second.ObjectAddress)
OS << format("\t%016" PRIx64, uint64_t(*Sym.second.ObjectAddress));
diff --git a/tools/dsymutil/DebugMap.h b/tools/dsymutil/DebugMap.h
index c9883773d3dc..d8de37e33d55 100644
--- a/tools/dsymutil/DebugMap.h
+++ b/tools/dsymutil/DebugMap.h
@@ -75,7 +75,7 @@ class DebugMapObject;
class DebugMap {
Triple BinaryTriple;
std::string BinaryPath;
-
+ std::vector<uint8_t> BinaryUUID;
using ObjectContainer = std::vector<std::unique_ptr<DebugMapObject>>;
ObjectContainer Objects;
@@ -89,8 +89,10 @@ class DebugMap {
///@}
public:
- DebugMap(const Triple &BinaryTriple, StringRef BinaryPath)
- : BinaryTriple(BinaryTriple), BinaryPath(BinaryPath) {}
+ DebugMap(const Triple &BinaryTriple, StringRef BinaryPath,
+ ArrayRef<uint8_t> BinaryUUID = ArrayRef<uint8_t>())
+ : BinaryTriple(BinaryTriple), BinaryPath(BinaryPath),
+ BinaryUUID(BinaryUUID.begin(), BinaryUUID.end()) {}
using const_iterator = ObjectContainer::const_iterator;
@@ -113,6 +115,10 @@ public:
const Triple &getTriple() const { return BinaryTriple; }
+ const ArrayRef<uint8_t> getUUID() const {
+ return ArrayRef<uint8_t>(BinaryUUID);
+ }
+
StringRef getBinaryPath() const { return BinaryPath; }
void print(raw_ostream &OS) const;
diff --git a/tools/dsymutil/DeclContext.h b/tools/dsymutil/DeclContext.h
index 1fa6815fa3b9..425c73671e12 100644
--- a/tools/dsymutil/DeclContext.h
+++ b/tools/dsymutil/DeclContext.h
@@ -7,6 +7,9 @@
//
//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H
+#define LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H
+
#include "CompileUnit.h"
#include "NonRelocatableStringpool.h"
#include "llvm/ADT/DenseMap.h"
@@ -16,9 +19,6 @@
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
#include "llvm/Support/Path.h"
-#ifndef LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H
-#define LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H
-
namespace llvm {
namespace dsymutil {
diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp
index 430e8e063e3c..0743cfc3ed40 100644
--- a/tools/dsymutil/DwarfLinker.cpp
+++ b/tools/dsymutil/DwarfLinker.cpp
@@ -155,6 +155,40 @@ static bool isODRAttribute(uint16_t Attr) {
llvm_unreachable("Improper attribute.");
}
+static bool isTypeTag(uint16_t Tag) {
+ switch (Tag) {
+ case dwarf::DW_TAG_array_type:
+ case dwarf::DW_TAG_class_type:
+ case dwarf::DW_TAG_enumeration_type:
+ case dwarf::DW_TAG_pointer_type:
+ case dwarf::DW_TAG_reference_type:
+ case dwarf::DW_TAG_string_type:
+ case dwarf::DW_TAG_structure_type:
+ case dwarf::DW_TAG_subroutine_type:
+ case dwarf::DW_TAG_typedef:
+ case dwarf::DW_TAG_union_type:
+ case dwarf::DW_TAG_ptr_to_member_type:
+ case dwarf::DW_TAG_set_type:
+ case dwarf::DW_TAG_subrange_type:
+ case dwarf::DW_TAG_base_type:
+ case dwarf::DW_TAG_const_type:
+ case dwarf::DW_TAG_constant:
+ case dwarf::DW_TAG_file_type:
+ case dwarf::DW_TAG_namelist:
+ case dwarf::DW_TAG_packed_type:
+ case dwarf::DW_TAG_volatile_type:
+ case dwarf::DW_TAG_restrict_type:
+ case dwarf::DW_TAG_atomic_type:
+ case dwarf::DW_TAG_interface_type:
+ case dwarf::DW_TAG_unspecified_type:
+ case dwarf::DW_TAG_shared_type:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
bool DwarfLinker::DIECloner::getDIENames(const DWARFDie &Die,
AttributesInfo &Info,
OffsetsStringPool &StringPool,
@@ -222,6 +256,7 @@ static bool analyzeContextInfo(const DWARFDie &DIE, unsigned ParentIdx,
CompileUnit &CU, DeclContext *CurrentDeclContext,
UniquingStringPool &StringPool,
DeclContextTree &Contexts,
+ uint64_t ModulesEndOffset,
bool InImportedModule = false) {
unsigned MyIdx = CU.getOrigUnit().getDIEIndex(DIE);
CompileUnit::DIEInfo &Info = CU.getInfo(MyIdx);
@@ -262,20 +297,27 @@ static bool analyzeContextInfo(const DWARFDie &DIE, unsigned ParentIdx,
Info.Prune = InImportedModule;
if (DIE.hasChildren())
for (auto Child : DIE.children())
- Info.Prune &= analyzeContextInfo(Child, MyIdx, CU, CurrentDeclContext,
- StringPool, Contexts, InImportedModule);
+ Info.Prune &=
+ analyzeContextInfo(Child, MyIdx, CU, CurrentDeclContext, StringPool,
+ Contexts, ModulesEndOffset, InImportedModule);
// Prune this DIE if it is either a forward declaration inside a
// DW_TAG_module or a DW_TAG_module that contains nothing but
// forward declarations.
Info.Prune &= (DIE.getTag() == dwarf::DW_TAG_module) ||
- dwarf::toUnsigned(DIE.find(dwarf::DW_AT_declaration), 0);
+ (isTypeTag(DIE.getTag()) &&
+ dwarf::toUnsigned(DIE.find(dwarf::DW_AT_declaration), 0));
- // Don't prune it if there is no definition for the DIE.
- Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset();
+ // Only prune forward declarations inside a DW_TAG_module for which a
+ // definition exists elsewhere.
+ if (ModulesEndOffset == 0)
+ Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset();
+ else
+ Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset() > 0 &&
+ Info.Ctxt->getCanonicalDIEOffset() <= ModulesEndOffset;
return Info.Prune;
-}
+} // namespace dsymutil
static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) {
switch (Tag) {
@@ -442,7 +484,7 @@ bool DwarfLinker::RelocationManager::findValidRelocs(
// the file, this allows us to just keep an index in the relocation
// array that we advance during our walk, rather than resorting to
// some associative container. See DwarfLinker::NextValidReloc.
- llvm::sort(ValidRelocs.begin(), ValidRelocs.end());
+ llvm::sort(ValidRelocs);
return true;
}
@@ -1258,40 +1300,6 @@ bool DwarfLinker::RelocationManager::applyValidRelocs(
return Applied;
}
-static bool isTypeTag(uint16_t Tag) {
- switch (Tag) {
- case dwarf::DW_TAG_array_type:
- case dwarf::DW_TAG_class_type:
- case dwarf::DW_TAG_enumeration_type:
- case dwarf::DW_TAG_pointer_type:
- case dwarf::DW_TAG_reference_type:
- case dwarf::DW_TAG_string_type:
- case dwarf::DW_TAG_structure_type:
- case dwarf::DW_TAG_subroutine_type:
- case dwarf::DW_TAG_typedef:
- case dwarf::DW_TAG_union_type:
- case dwarf::DW_TAG_ptr_to_member_type:
- case dwarf::DW_TAG_set_type:
- case dwarf::DW_TAG_subrange_type:
- case dwarf::DW_TAG_base_type:
- case dwarf::DW_TAG_const_type:
- case dwarf::DW_TAG_constant:
- case dwarf::DW_TAG_file_type:
- case dwarf::DW_TAG_namelist:
- case dwarf::DW_TAG_packed_type:
- case dwarf::DW_TAG_volatile_type:
- case dwarf::DW_TAG_restrict_type:
- case dwarf::DW_TAG_atomic_type:
- case dwarf::DW_TAG_interface_type:
- case dwarf::DW_TAG_unspecified_type:
- case dwarf::DW_TAG_shared_type:
- return true;
- default:
- break;
- }
- return false;
-}
-
static bool isObjCSelector(StringRef Name) {
return Name.size() > 2 && (Name[0] == '-' || Name[0] == '+') &&
(Name[1] == '[');
@@ -1693,10 +1701,12 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit,
DWARFDataExtractor LineExtractor(
OrigDwarf.getDWARFObj(), OrigDwarf.getDWARFObj().getLineSection(),
OrigDwarf.isLittleEndian(), Unit.getOrigUnit().getAddressByteSize());
+ if (Options.Translator)
+ return Streamer->translateLineTable(LineExtractor, StmtOffset, Options);
Error Err = LineTable.parse(LineExtractor, &StmtOffset, OrigDwarf,
- &Unit.getOrigUnit());
- DWARFDebugLine::warn(std::move(Err));
+ &Unit.getOrigUnit(), DWARFContext::dumpWarning);
+ DWARFContext::dumpWarning(std::move(Err));
// This vector is the output line table.
std::vector<DWARFDebugLine::Row> NewRows;
@@ -2024,7 +2034,7 @@ bool DwarfLinker::registerModuleReference(
const DWARFDie &CUDie, const DWARFUnit &Unit, DebugMap &ModuleMap,
const DebugMapObject &DMO, RangesTy &Ranges, OffsetsStringPool &StringPool,
UniquingStringPool &UniquingStringPool, DeclContextTree &ODRContexts,
- unsigned &UnitID, unsigned Indent) {
+ uint64_t ModulesEndOffset, unsigned &UnitID, unsigned Indent, bool Quiet) {
std::string PCMfile = dwarf::toString(
CUDie.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), "");
if (PCMfile.empty())
@@ -2036,11 +2046,12 @@ bool DwarfLinker::registerModuleReference(
std::string Name = dwarf::toString(CUDie.find(dwarf::DW_AT_name), "");
if (Name.empty()) {
- reportWarning("Anonymous module skeleton CU for " + PCMfile, DMO);
+ if (!Quiet)
+ reportWarning("Anonymous module skeleton CU for " + PCMfile, DMO);
return true;
}
- if (Options.Verbose) {
+ if (!Quiet && Options.Verbose) {
outs().indent(Indent);
outs() << "Found clang module reference " << PCMfile;
}
@@ -2050,24 +2061,25 @@ bool DwarfLinker::registerModuleReference(
// FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is
// fixed in clang, only warn about DWO_id mismatches in verbose mode.
// ASTFileSignatures will change randomly when a module is rebuilt.
- if (Options.Verbose && (Cached->second != DwoId))
+ if (!Quiet && Options.Verbose && (Cached->second != DwoId))
reportWarning(Twine("hash mismatch: this object file was built against a "
"different version of the module ") +
PCMfile,
DMO);
- if (Options.Verbose)
+ if (!Quiet && Options.Verbose)
outs() << " [cached].\n";
return true;
}
- if (Options.Verbose)
+ if (!Quiet && Options.Verbose)
outs() << " ...\n";
// Cyclic dependencies are disallowed by Clang, but we still
// shouldn't run into an infinite loop, so mark it as processed now.
ClangModules.insert({PCMfile, DwoId});
- if (Error E = loadClangModule(PCMfile, PCMpath, Name, DwoId, ModuleMap, DMO,
- Ranges, StringPool, UniquingStringPool,
- ODRContexts, UnitID, Indent + 2)) {
+ if (Error E =
+ loadClangModule(PCMfile, PCMpath, Name, DwoId, ModuleMap, DMO, Ranges,
+ StringPool, UniquingStringPool, ODRContexts,
+ ModulesEndOffset, UnitID, Indent + 2, Quiet)) {
consumeError(std::move(E));
return false;
}
@@ -2096,14 +2108,12 @@ DwarfLinker::loadObject(const DebugMapObject &Obj, const DebugMap &Map) {
return *Object;
}
-Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath,
- StringRef ModuleName, uint64_t DwoId,
- DebugMap &ModuleMap,
- const DebugMapObject &DMO, RangesTy &Ranges,
- OffsetsStringPool &StringPool,
- UniquingStringPool &UniquingStringPool,
- DeclContextTree &ODRContexts,
- unsigned &UnitID, unsigned Indent) {
+Error DwarfLinker::loadClangModule(
+ StringRef Filename, StringRef ModulePath, StringRef ModuleName,
+ uint64_t DwoId, DebugMap &ModuleMap, const DebugMapObject &DMO,
+ RangesTy &Ranges, OffsetsStringPool &StringPool,
+ UniquingStringPool &UniquingStringPool, DeclContextTree &ODRContexts,
+ uint64_t ModulesEndOffset, unsigned &UnitID, unsigned Indent, bool Quiet) {
SmallString<80> Path(Options.PrependPath);
if (sys::path::is_relative(Filename))
sys::path::append(Path, ModulePath, Filename);
@@ -2164,8 +2174,8 @@ Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath,
if (!CUDie)
continue;
if (!registerModuleReference(CUDie, *CU, ModuleMap, DMO, Ranges, StringPool,
- UniquingStringPool, ODRContexts, UnitID,
- Indent)) {
+ UniquingStringPool, ODRContexts,
+ ModulesEndOffset, UnitID, Indent, Quiet)) {
if (Unit) {
std::string Err =
(Filename +
@@ -2179,7 +2189,7 @@ Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath,
// ASTFileSignatures will change randomly when a module is rebuilt.
uint64_t PCMDwoId = getDwoId(CUDie, *CU);
if (PCMDwoId != DwoId) {
- if (Options.Verbose)
+ if (!Quiet && Options.Verbose)
reportWarning(
Twine("hash mismatch: this object file was built against a "
"different version of the module ") +
@@ -2194,14 +2204,14 @@ Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath,
ModuleName);
Unit->setHasInterestingContent();
analyzeContextInfo(CUDie, 0, *Unit, &ODRContexts.getRoot(),
- UniquingStringPool, ODRContexts);
+ UniquingStringPool, ODRContexts, ModulesEndOffset);
// Keep everything.
Unit->markEverythingAsKept();
}
}
if (!Unit->getOrigUnit().getUnitDIE().hasChildren())
return Error::success();
- if (Options.Verbose) {
+ if (!Quiet && Options.Verbose) {
outs().indent(Indent);
outs() << "cloning .debug_info from " << Filename << "\n";
}
@@ -2237,17 +2247,16 @@ void DwarfLinker::DIECloner::cloneAllCompileUnits(
if (Linker.Options.NoOutput)
continue;
- if (LLVM_LIKELY(!Linker.Options.Update)) {
- // FIXME: for compatibility with the classic dsymutil, we emit an empty
- // line table for the unit, even if the unit doesn't actually exist in
- // the DIE tree.
+ // FIXME: for compatibility with the classic dsymutil, we emit
+ // an empty line table for the unit, even if the unit doesn't
+ // actually exist in the DIE tree.
+ if (LLVM_LIKELY(!Linker.Options.Update) || Linker.Options.Translator)
Linker.patchLineTableForUnit(*CurrentUnit, DwarfContext, Ranges, DMO);
- Linker.emitAcceleratorEntriesForUnit(*CurrentUnit);
- Linker.patchRangesForUnit(*CurrentUnit, DwarfContext, DMO);
- Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext);
- } else {
- Linker.emitAcceleratorEntriesForUnit(*CurrentUnit);
- }
+ Linker.emitAcceleratorEntriesForUnit(*CurrentUnit);
+ if (Linker.Options.Update)
+ continue;
+ Linker.patchRangesForUnit(*CurrentUnit, DwarfContext, DMO);
+ Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext);
}
if (Linker.Options.NoOutput)
@@ -2372,7 +2381,7 @@ bool DwarfLinker::link(const DebugMap &Map) {
// This Dwarf string pool which is used for emission. It must be used
// serially as the order of calling getStringOffset matters for
// reproducibility.
- OffsetsStringPool OffsetsStringPool;
+ OffsetsStringPool OffsetsStringPool(Options.Translator);
// ODR Contexts for the link.
DeclContextTree ODRContexts;
@@ -2409,15 +2418,20 @@ bool DwarfLinker::link(const DebugMap &Map) {
warn(Err.message());
continue;
}
- if (!Options.NoTimestamp &&
- Stat.getLastModificationTime() !=
- sys::TimePoint<>(LinkContext.DMO.getTimestamp())) {
- // Not using the helper here as we can easily stream TimePoint<>.
- WithColor::warning()
- << "Timestamp mismatch for " << File << ": "
- << Stat.getLastModificationTime() << " and "
- << sys::TimePoint<>(LinkContext.DMO.getTimestamp()) << "\n";
- continue;
+ if (!Options.NoTimestamp) {
+ // The modification can have sub-second precision so we need to cast
+ // away the extra precision that's not present in the debug map.
+ auto ModificationTime =
+ std::chrono::time_point_cast<std::chrono::seconds>(
+ Stat.getLastModificationTime());
+ if (ModificationTime != LinkContext.DMO.getTimestamp()) {
+ // Not using the helper here as we can easily stream TimePoint<>.
+ WithColor::warning()
+ << "Timestamp mismatch for " << File << ": "
+ << Stat.getLastModificationTime() << " and "
+ << sys::TimePoint<>(LinkContext.DMO.getTimestamp()) << "\n";
+ continue;
+ }
}
// Copy the module into the .swift_ast section.
@@ -2466,14 +2480,10 @@ bool DwarfLinker::link(const DebugMap &Map) {
DumpOpts.Verbose = Options.Verbose;
CUDie.dump(outs(), 0, DumpOpts);
}
-
- if (!CUDie || LLVM_UNLIKELY(Options.Update) ||
- !registerModuleReference(CUDie, *CU, ModuleMap, LinkContext.DMO,
- LinkContext.Ranges, OffsetsStringPool,
- UniquingStringPool, ODRContexts, UnitID)) {
- LinkContext.CompileUnits.push_back(llvm::make_unique<CompileUnit>(
- *CU, UnitID++, !Options.NoODR && !Options.Update, ""));
- }
+ if (CUDie && !LLVM_UNLIKELY(Options.Update))
+ registerModuleReference(CUDie, *CU, ModuleMap, LinkContext.DMO,
+ LinkContext.Ranges, OffsetsStringPool,
+ UniquingStringPool, ODRContexts, 0, UnitID);
}
}
@@ -2481,37 +2491,54 @@ bool DwarfLinker::link(const DebugMap &Map) {
if (MaxDwarfVersion == 0)
MaxDwarfVersion = 3;
+ // At this point we know how much data we have emitted. We use this value to
+ // compare canonical DIE offsets in analyzeContextInfo to see if a definition
+ // is already emitted, without being affected by canonical die offsets set
+ // later. This prevents undeterminism when analyze and clone execute
+ // concurrently, as clone set the canonical DIE offset and analyze reads it.
+ const uint64_t ModulesEndOffset = OutputDebugInfoSize;
+
// These variables manage the list of processed object files.
// The mutex and condition variable are to ensure that this is thread safe.
std::mutex ProcessedFilesMutex;
std::condition_variable ProcessedFilesConditionVariable;
BitVector ProcessedFiles(NumObjects, false);
- // Now do analyzeContextInfo in parallel as it is particularly expensive.
- auto AnalyzeLambda = [&]() {
- for (unsigned i = 0, e = NumObjects; i != e; ++i) {
- auto &LinkContext = ObjectContexts[i];
+ // Analyzing the context info is particularly expensive so it is executed in
+ // parallel with emitting the previous compile unit.
+ auto AnalyzeLambda = [&](size_t i) {
+ auto &LinkContext = ObjectContexts[i];
- if (!LinkContext.ObjectFile) {
- std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex);
- ProcessedFiles.set(i);
- ProcessedFilesConditionVariable.notify_one();
- continue;
- }
+ if (!LinkContext.ObjectFile || !LinkContext.DwarfContext)
+ return;
- // Now build the DIE parent links that we will use during the next phase.
- for (auto &CurrentUnit : LinkContext.CompileUnits) {
- auto CUDie = CurrentUnit->getOrigUnit().getUnitDIE();
- if (!CUDie)
- continue;
- analyzeContextInfo(CurrentUnit->getOrigUnit().getUnitDIE(), 0,
- *CurrentUnit, &ODRContexts.getRoot(),
- UniquingStringPool, ODRContexts);
+ for (const auto &CU : LinkContext.DwarfContext->compile_units()) {
+ updateDwarfVersion(CU->getVersion());
+ // The !registerModuleReference() condition effectively skips
+ // over fully resolved skeleton units. This second pass of
+ // registerModuleReferences doesn't do any new work, but it
+ // will collect top-level errors, which are suppressed. Module
+ // warnings were already displayed in the first iteration.
+ bool Quiet = true;
+ auto CUDie = CU->getUnitDIE(false);
+ if (!CUDie || LLVM_UNLIKELY(Options.Update) ||
+ !registerModuleReference(CUDie, *CU, ModuleMap, LinkContext.DMO,
+ LinkContext.Ranges, OffsetsStringPool,
+ UniquingStringPool, ODRContexts,
+ ModulesEndOffset, UnitID, Quiet)) {
+ LinkContext.CompileUnits.push_back(llvm::make_unique<CompileUnit>(
+ *CU, UnitID++, !Options.NoODR && !Options.Update, ""));
}
+ }
- std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex);
- ProcessedFiles.set(i);
- ProcessedFilesConditionVariable.notify_one();
+ // Now build the DIE parent links that we will use during the next phase.
+ for (auto &CurrentUnit : LinkContext.CompileUnits) {
+ auto CUDie = CurrentUnit->getOrigUnit().getUnitDIE();
+ if (!CUDie)
+ continue;
+ analyzeContextInfo(CurrentUnit->getOrigUnit().getUnitDIE(), 0,
+ *CurrentUnit, &ODRContexts.getRoot(),
+ UniquingStringPool, ODRContexts, ModulesEndOffset);
}
};
@@ -2519,57 +2546,48 @@ bool DwarfLinker::link(const DebugMap &Map) {
// Note, although this loop runs in serial, it can run in parallel with
// the analyzeContextInfo loop so long as we process files with indices >=
// than those processed by analyzeContextInfo.
- auto CloneLambda = [&]() {
- for (unsigned i = 0, e = NumObjects; i != e; ++i) {
- {
- std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex);
- if (!ProcessedFiles[i]) {
- ProcessedFilesConditionVariable.wait(
- LockGuard, [&]() { return ProcessedFiles[i]; });
- }
- }
-
- auto &LinkContext = ObjectContexts[i];
- if (!LinkContext.ObjectFile)
- continue;
-
- // Then mark all the DIEs that need to be present in the linked output
- // and collect some information about them.
- // Note that this loop can not be merged with the previous one because
- // cross-cu references require the ParentIdx to be setup for every CU in
- // the object file before calling this.
- if (LLVM_UNLIKELY(Options.Update)) {
- for (auto &CurrentUnit : LinkContext.CompileUnits)
- CurrentUnit->markEverythingAsKept();
- Streamer->copyInvariantDebugSection(*LinkContext.ObjectFile);
- } else {
- for (auto &CurrentUnit : LinkContext.CompileUnits)
- lookForDIEsToKeep(LinkContext.RelocMgr, LinkContext.Ranges,
- LinkContext.CompileUnits,
- CurrentUnit->getOrigUnit().getUnitDIE(),
- LinkContext.DMO, *CurrentUnit, 0);
- }
+ auto CloneLambda = [&](size_t i) {
+ auto &LinkContext = ObjectContexts[i];
+ if (!LinkContext.ObjectFile)
+ return;
- // The calls to applyValidRelocs inside cloneDIE will walk the reloc
- // array again (in the same way findValidRelocsInDebugInfo() did). We
- // need to reset the NextValidReloc index to the beginning.
- LinkContext.RelocMgr.resetValidRelocs();
- if (LinkContext.RelocMgr.hasValidRelocs() ||
- LLVM_UNLIKELY(Options.Update))
- DIECloner(*this, LinkContext.RelocMgr, DIEAlloc,
- LinkContext.CompileUnits, Options)
- .cloneAllCompileUnits(*LinkContext.DwarfContext, LinkContext.DMO,
- LinkContext.Ranges, OffsetsStringPool);
- if (!Options.NoOutput && !LinkContext.CompileUnits.empty() &&
- LLVM_LIKELY(!Options.Update))
- patchFrameInfoForObject(
- LinkContext.DMO, LinkContext.Ranges, *LinkContext.DwarfContext,
- LinkContext.CompileUnits[0]->getOrigUnit().getAddressByteSize());
-
- // Clean-up before starting working on the next object.
- endDebugObject(LinkContext);
+ // Then mark all the DIEs that need to be present in the linked output
+ // and collect some information about them.
+ // Note that this loop can not be merged with the previous one because
+ // cross-cu references require the ParentIdx to be setup for every CU in
+ // the object file before calling this.
+ if (LLVM_UNLIKELY(Options.Update)) {
+ for (auto &CurrentUnit : LinkContext.CompileUnits)
+ CurrentUnit->markEverythingAsKept();
+ Streamer->copyInvariantDebugSection(*LinkContext.ObjectFile);
+ } else {
+ for (auto &CurrentUnit : LinkContext.CompileUnits)
+ lookForDIEsToKeep(LinkContext.RelocMgr, LinkContext.Ranges,
+ LinkContext.CompileUnits,
+ CurrentUnit->getOrigUnit().getUnitDIE(),
+ LinkContext.DMO, *CurrentUnit, 0);
}
+ // The calls to applyValidRelocs inside cloneDIE will walk the reloc
+ // array again (in the same way findValidRelocsInDebugInfo() did). We
+ // need to reset the NextValidReloc index to the beginning.
+ LinkContext.RelocMgr.resetValidRelocs();
+ if (LinkContext.RelocMgr.hasValidRelocs() || LLVM_UNLIKELY(Options.Update))
+ DIECloner(*this, LinkContext.RelocMgr, DIEAlloc, LinkContext.CompileUnits,
+ Options)
+ .cloneAllCompileUnits(*LinkContext.DwarfContext, LinkContext.DMO,
+ LinkContext.Ranges, OffsetsStringPool);
+ if (!Options.NoOutput && !LinkContext.CompileUnits.empty() &&
+ LLVM_LIKELY(!Options.Update))
+ patchFrameInfoForObject(
+ LinkContext.DMO, LinkContext.Ranges, *LinkContext.DwarfContext,
+ LinkContext.CompileUnits[0]->getOrigUnit().getAddressByteSize());
+
+ // Clean-up before starting working on the next object.
+ endDebugObject(LinkContext);
+ };
+
+ auto EmitLambda = [&]() {
// Emit everything that's global.
if (!Options.NoOutput) {
Streamer->emitAbbrevs(Abbreviations, MaxDwarfVersion);
@@ -2591,20 +2609,48 @@ bool DwarfLinker::link(const DebugMap &Map) {
}
};
- // FIXME: The DwarfLinker can have some very deep recursion that can max
- // out the (significantly smaller) stack when using threads. We don't
- // want this limitation when we only have a single thread.
+ auto AnalyzeAll = [&]() {
+ for (unsigned i = 0, e = NumObjects; i != e; ++i) {
+ AnalyzeLambda(i);
+
+ std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex);
+ ProcessedFiles.set(i);
+ ProcessedFilesConditionVariable.notify_one();
+ }
+ };
+
+ auto CloneAll = [&]() {
+ for (unsigned i = 0, e = NumObjects; i != e; ++i) {
+ {
+ std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex);
+ if (!ProcessedFiles[i]) {
+ ProcessedFilesConditionVariable.wait(
+ LockGuard, [&]() { return ProcessedFiles[i]; });
+ }
+ }
+
+ CloneLambda(i);
+ }
+ EmitLambda();
+ };
+
+ // To limit memory usage in the single threaded case, analyze and clone are
+ // run sequentially so the LinkContext is freed after processing each object
+ // in endDebugObject.
if (Options.Threads == 1) {
- AnalyzeLambda();
- CloneLambda();
+ for (unsigned i = 0, e = NumObjects; i != e; ++i) {
+ AnalyzeLambda(i);
+ CloneLambda(i);
+ }
+ EmitLambda();
} else {
ThreadPool pool(2);
- pool.async(AnalyzeLambda);
- pool.async(CloneLambda);
+ pool.async(AnalyzeAll);
+ pool.async(CloneAll);
pool.wait();
}
- return Options.NoOutput ? true : Streamer->finish(Map);
+ return Options.NoOutput ? true : Streamer->finish(Map, Options.Translator);
} // namespace dsymutil
bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder,
diff --git a/tools/dsymutil/DwarfLinker.h b/tools/dsymutil/DwarfLinker.h
index b1a950ff0fa2..64fa8d2107d5 100644
--- a/tools/dsymutil/DwarfLinker.h
+++ b/tools/dsymutil/DwarfLinker.h
@@ -199,8 +199,9 @@ private:
RangesTy &Ranges,
OffsetsStringPool &OffsetsStringPool,
UniquingStringPool &UniquingStringPoolStringPool,
- DeclContextTree &ODRContexts, unsigned &UnitID,
- unsigned Indent = 0);
+ DeclContextTree &ODRContexts,
+ uint64_t ModulesEndOffset, unsigned &UnitID,
+ unsigned Indent = 0, bool Quiet = false);
/// Recursively add the debug info in this clang module .pcm
/// file (and all the modules imported by it in a bottom-up fashion)
@@ -210,8 +211,9 @@ private:
DebugMap &ModuleMap, const DebugMapObject &DMO,
RangesTy &Ranges, OffsetsStringPool &OffsetsStringPool,
UniquingStringPool &UniquingStringPool,
- DeclContextTree &ODRContexts, unsigned &UnitID,
- unsigned Indent = 0);
+ DeclContextTree &ODRContexts, uint64_t ModulesEndOffset,
+ unsigned &UnitID, unsigned Indent = 0,
+ bool Quiet = false);
/// Flags passed to DwarfLinker::lookForDIEsToKeep
enum TraversalFlags {
diff --git a/tools/dsymutil/DwarfStreamer.cpp b/tools/dsymutil/DwarfStreamer.cpp
index 7350d19e17bf..28088ff3369c 100644
--- a/tools/dsymutil/DwarfStreamer.cpp
+++ b/tools/dsymutil/DwarfStreamer.cpp
@@ -124,11 +124,11 @@ bool DwarfStreamer::init(Triple TheTriple) {
return true;
}
-bool DwarfStreamer::finish(const DebugMap &DM) {
+bool DwarfStreamer::finish(const DebugMap &DM, SymbolMapTranslator &T) {
bool Result = true;
if (DM.getTriple().isOSDarwin() && !DM.getBinaryPath().empty() &&
Options.FileType == OutputFileType::Object)
- Result = MachOUtils::generateDsymCompanion(DM, *MS, OutFile);
+ Result = MachOUtils::generateDsymCompanion(DM, T, *MS, OutFile);
else
MS->Finish();
return Result;
@@ -190,10 +190,8 @@ void DwarfStreamer::emitDIE(DIE &Die) {
/// Emit the debug_str section stored in \p Pool.
void DwarfStreamer::emitStrings(const NonRelocatableStringpool &Pool) {
Asm->OutStreamer->SwitchSection(MOFI->getDwarfStrSection());
- std::vector<DwarfStringPoolEntryRef> Entries = Pool.getEntries();
+ std::vector<DwarfStringPoolEntryRef> Entries = Pool.getEntriesForEmission();
for (auto Entry : Entries) {
- if (Entry.getIndex() == -1U)
- break;
// Emit the string itself.
Asm->OutStreamer->EmitBytes(Entry.getString());
// Emit a null terminator.
@@ -322,7 +320,7 @@ void DwarfStreamer::emitUnitRangesEntries(CompileUnit &Unit,
// The object addresses where sorted, but again, the linked
// addresses might end up in a different order.
- llvm::sort(Ranges.begin(), Ranges.end());
+ llvm::sort(Ranges);
if (!Ranges.empty()) {
MS->SwitchSection(MC->getObjectFileInfo()->getDwarfARangesSection());
@@ -579,6 +577,89 @@ void DwarfStreamer::emitLineTableForUnit(MCDwarfLineTableParams Params,
MS->EmitLabel(LineEndSym);
}
+/// Copy the debug_line over to the updated binary while unobfuscating the file
+/// names and directories.
+void DwarfStreamer::translateLineTable(DataExtractor Data, uint32_t Offset,
+ LinkOptions &Options) {
+ MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection());
+ StringRef Contents = Data.getData();
+
+ // We have to deconstruct the line table header, because it contains to
+ // length fields that will need to be updated when we change the length of
+ // the files and directories in there.
+ unsigned UnitLength = Data.getU32(&Offset);
+ unsigned UnitEnd = Offset + UnitLength;
+ MCSymbol *BeginLabel = MC->createTempSymbol();
+ MCSymbol *EndLabel = MC->createTempSymbol();
+ unsigned Version = Data.getU16(&Offset);
+
+ if (Version > 5) {
+ warn("Unsupported line table version: dropping contents and not "
+ "unobfsucating line table.");
+ return;
+ }
+
+ Asm->EmitLabelDifference(EndLabel, BeginLabel, 4);
+ Asm->OutStreamer->EmitLabel(BeginLabel);
+ Asm->emitInt16(Version);
+ LineSectionSize += 6;
+
+ MCSymbol *HeaderBeginLabel = MC->createTempSymbol();
+ MCSymbol *HeaderEndLabel = MC->createTempSymbol();
+ Asm->EmitLabelDifference(HeaderEndLabel, HeaderBeginLabel, 4);
+ Asm->OutStreamer->EmitLabel(HeaderBeginLabel);
+ Offset += 4;
+ LineSectionSize += 4;
+
+ uint32_t AfterHeaderLengthOffset = Offset;
+ // Skip to the directories.
+ Offset += (Version >= 4) ? 5 : 4;
+ unsigned OpcodeBase = Data.getU8(&Offset);
+ Offset += OpcodeBase - 1;
+ Asm->OutStreamer->EmitBytes(Contents.slice(AfterHeaderLengthOffset, Offset));
+ LineSectionSize += Offset - AfterHeaderLengthOffset;
+
+ // Offset points to the first directory.
+ while (const char *Dir = Data.getCStr(&Offset)) {
+ if (Dir[0] == 0)
+ break;
+
+ StringRef Translated = Options.Translator(Dir);
+ Asm->OutStreamer->EmitBytes(Translated);
+ Asm->emitInt8(0);
+ LineSectionSize += Translated.size() + 1;
+ }
+ Asm->emitInt8(0);
+ LineSectionSize += 1;
+
+ while (const char *File = Data.getCStr(&Offset)) {
+ if (File[0] == 0)
+ break;
+
+ StringRef Translated = Options.Translator(File);
+ Asm->OutStreamer->EmitBytes(Translated);
+ Asm->emitInt8(0);
+ LineSectionSize += Translated.size() + 1;
+
+ uint32_t OffsetBeforeLEBs = Offset;
+ Asm->EmitULEB128(Data.getULEB128(&Offset));
+ Asm->EmitULEB128(Data.getULEB128(&Offset));
+ Asm->EmitULEB128(Data.getULEB128(&Offset));
+ LineSectionSize += Offset - OffsetBeforeLEBs;
+ }
+ Asm->emitInt8(0);
+ LineSectionSize += 1;
+
+ Asm->OutStreamer->EmitLabel(HeaderEndLabel);
+
+ // Copy the actual line table program over.
+ Asm->OutStreamer->EmitBytes(Contents.slice(Offset, UnitEnd));
+ LineSectionSize += UnitEnd - Offset;
+
+ Asm->OutStreamer->EmitLabel(EndLabel);
+ Offset = UnitEnd;
+}
+
static void emitSectionContents(const object::ObjectFile &Obj,
StringRef SecName, MCStreamer *MS) {
StringRef Contents;
@@ -588,8 +669,10 @@ static void emitSectionContents(const object::ObjectFile &Obj,
}
void DwarfStreamer::copyInvariantDebugSection(const object::ObjectFile &Obj) {
- MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection());
- emitSectionContents(Obj, "debug_line", MS);
+ if (!Options.Translator) {
+ MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection());
+ emitSectionContents(Obj, "debug_line", MS);
+ }
MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection());
emitSectionContents(Obj, "debug_loc", MS);
diff --git a/tools/dsymutil/DwarfStreamer.h b/tools/dsymutil/DwarfStreamer.h
index 74f0a09001d0..abc86547ef6f 100644
--- a/tools/dsymutil/DwarfStreamer.h
+++ b/tools/dsymutil/DwarfStreamer.h
@@ -7,6 +7,9 @@
//
//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H
+#define LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H
+
#include "CompileUnit.h"
#include "DebugMap.h"
#include "LinkUtils.h"
@@ -32,9 +35,6 @@
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
-#ifndef LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H
-#define LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H
-
namespace llvm {
namespace dsymutil {
@@ -50,7 +50,7 @@ public:
bool init(Triple TheTriple);
/// Dump the file to the disk.
- bool finish(const DebugMap &);
+ bool finish(const DebugMap &, SymbolMapTranslator &T);
AsmPrinter &getAsmPrinter() const { return *Asm; }
@@ -104,6 +104,11 @@ public:
std::vector<DWARFDebugLine::Row> &Rows,
unsigned AdddressSize);
+ /// Copy the debug_line over to the updated binary while unobfuscating the
+ /// file names and directories.
+ void translateLineTable(DataExtractor LineData, uint32_t Offset,
+ LinkOptions &Options);
+
/// Copy over the debug sections that are not modified when updating.
void copyInvariantDebugSection(const object::ObjectFile &Obj);
diff --git a/tools/dsymutil/LinkUtils.h b/tools/dsymutil/LinkUtils.h
index f0abd888b529..07697418535c 100644
--- a/tools/dsymutil/LinkUtils.h
+++ b/tools/dsymutil/LinkUtils.h
@@ -10,8 +10,11 @@
#ifndef LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H
#define LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H
+#include "SymbolMap.h"
+
#include "llvm/ADT/Twine.h"
#include "llvm/Support/WithColor.h"
+
#include <string>
namespace llvm {
@@ -60,6 +63,9 @@ struct LinkOptions {
/// -oso-prepend-path
std::string PrependPath;
+ /// Symbol map translator.
+ SymbolMapTranslator Translator;
+
LinkOptions() = default;
};
diff --git a/tools/dsymutil/MachODebugMapParser.cpp b/tools/dsymutil/MachODebugMapParser.cpp
index 48155b402042..8ff7e22da228 100644
--- a/tools/dsymutil/MachODebugMapParser.cpp
+++ b/tools/dsymutil/MachODebugMapParser.cpp
@@ -163,7 +163,8 @@ std::unique_ptr<DebugMap>
MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary,
StringRef BinaryPath) {
loadMainBinarySymbols(MainBinary);
- Result = make_unique<DebugMap>(MainBinary.getArchTriple(), BinaryPath);
+ ArrayRef<uint8_t> UUID = MainBinary.getUuid();
+ Result = make_unique<DebugMap>(MainBinary.getArchTriple(), BinaryPath, UUID);
MainBinaryStrings = MainBinary.getStringTableData();
for (const SymbolRef &Symbol : MainBinary.symbols()) {
const DataRefImpl &DRI = Symbol.getRawDataRefImpl();
@@ -511,14 +512,16 @@ void MachODebugMapParser::loadMainBinarySymbols(
// Skip undefined and STAB entries.
if ((Type == SymbolRef::ST_Debug) || (Type == SymbolRef::ST_Unknown))
continue;
- // The only symbols of interest are the global variables. These
- // are the only ones that need to be queried because the address
- // of common data won't be described in the debug map. All other
- // addresses should be fetched for the debug map.
+ // In theory, the only symbols of interest are the global variables. These
+ // are the only ones that need to be queried because the address of common
+ // data won't be described in the debug map. All other addresses should be
+ // fetched for the debug map. In reality, by playing with 'ld -r' and
+ // export lists, you can get symbols described as N_GSYM in the debug map,
+ // but associated with a local symbol. Gather all the symbols, but prefer
+ // the global ones.
uint8_t SymType =
MainBinary.getSymbolTableEntry(Sym.getRawDataRefImpl()).n_type;
- if (!(SymType & (MachO::N_EXT | MachO::N_PEXT)))
- continue;
+ bool Extern = SymType & (MachO::N_EXT | MachO::N_PEXT);
Expected<section_iterator> SectionOrErr = Sym.getSection();
if (!SectionOrErr) {
// TODO: Actually report errors helpfully.
@@ -538,7 +541,11 @@ void MachODebugMapParser::loadMainBinarySymbols(
StringRef Name = *NameOrErr;
if (Name.size() == 0 || Name[0] == '\0')
continue;
- MainBinarySymbolAddresses[Name] = Addr;
+ // Override only if the new key is global.
+ if (Extern)
+ MainBinarySymbolAddresses[Name] = Addr;
+ else
+ MainBinarySymbolAddresses.try_emplace(Name, Addr);
}
}
diff --git a/tools/dsymutil/MachOUtils.cpp b/tools/dsymutil/MachOUtils.cpp
index fc498cc49c19..8c54563eab98 100644
--- a/tools/dsymutil/MachOUtils.cpp
+++ b/tools/dsymutil/MachOUtils.cpp
@@ -333,8 +333,8 @@ static unsigned segmentLoadCommandSize(bool Is64Bit, unsigned NumSections) {
// Stream a dSYM companion binary file corresponding to the binary referenced
// by \a DM to \a OutFile. The passed \a MS MCStreamer is setup to write to
// \a OutFile and it must be using a MachObjectWriter object to do so.
-bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
- raw_fd_ostream &OutFile) {
+bool generateDsymCompanion(const DebugMap &DM, SymbolMapTranslator &Translator,
+ MCStreamer &MS, raw_fd_ostream &OutFile) {
auto &ObjectStreamer = static_cast<MCObjectStreamer &>(MS);
MCAssembler &MCAsm = ObjectStreamer.getAssembler();
auto &Writer = static_cast<MachObjectWriter &>(MCAsm.getWriter());
@@ -368,25 +368,37 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
bool Is64Bit = Writer.is64Bit();
MachO::symtab_command SymtabCmd = InputBinary.getSymtabLoadCommand();
- // Get UUID.
+ // Compute the number of load commands we will need.
+ unsigned LoadCommandSize = 0;
+ unsigned NumLoadCommands = 0;
+
+ // Get LC_UUID and LC_BUILD_VERSION.
MachO::uuid_command UUIDCmd;
+ SmallVector<MachO::build_version_command, 2> BuildVersionCmd;
memset(&UUIDCmd, 0, sizeof(UUIDCmd));
- UUIDCmd.cmd = MachO::LC_UUID;
- UUIDCmd.cmdsize = sizeof(MachO::uuid_command);
for (auto &LCI : InputBinary.load_commands()) {
- if (LCI.C.cmd == MachO::LC_UUID) {
+ switch (LCI.C.cmd) {
+ case MachO::LC_UUID:
+ if (UUIDCmd.cmd)
+ return error("Binary contains more than one UUID");
UUIDCmd = InputBinary.getUuidCommand(LCI);
+ ++NumLoadCommands;
+ LoadCommandSize += sizeof(UUIDCmd);
+ break;
+ case MachO::LC_BUILD_VERSION: {
+ MachO::build_version_command Cmd;
+ memset(&Cmd, 0, sizeof(Cmd));
+ Cmd = InputBinary.getBuildVersionLoadCommand(LCI);
+ ++NumLoadCommands;
+ LoadCommandSize += sizeof(Cmd);
+ // LLDB doesn't care about the build tools for now.
+ Cmd.ntools = 0;
+ BuildVersionCmd.push_back(Cmd);
+ break;
+ }
+ default:
break;
}
- }
-
- // Compute the number of load commands we will need.
- unsigned LoadCommandSize = 0;
- unsigned NumLoadCommands = 0;
- // We will copy the UUID if there is one.
- if (UUIDCmd.cmd != 0) {
- ++NumLoadCommands;
- LoadCommandSize += sizeof(MachO::uuid_command);
}
// If we have a valid symtab to copy, do it.
@@ -431,7 +443,7 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
}
SmallString<0> NewSymtab;
- NonRelocatableStringpool NewStrings;
+ NonRelocatableStringpool NewStrings(Translator);
unsigned NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist);
unsigned NumSyms = 0;
uint64_t NewStringsSize = 0;
@@ -452,10 +464,18 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
assert(OutFile.tell() == HeaderSize);
if (UUIDCmd.cmd != 0) {
Writer.W.write<uint32_t>(UUIDCmd.cmd);
- Writer.W.write<uint32_t>(UUIDCmd.cmdsize);
+ Writer.W.write<uint32_t>(sizeof(UUIDCmd));
OutFile.write(reinterpret_cast<const char *>(UUIDCmd.uuid), 16);
assert(OutFile.tell() == HeaderSize + sizeof(UUIDCmd));
}
+ for (auto Cmd : BuildVersionCmd) {
+ Writer.W.write<uint32_t>(Cmd.cmd);
+ Writer.W.write<uint32_t>(sizeof(Cmd));
+ Writer.W.write<uint32_t>(Cmd.platform);
+ Writer.W.write<uint32_t>(Cmd.minos);
+ Writer.W.write<uint32_t>(Cmd.sdk);
+ Writer.W.write<uint32_t>(Cmd.ntools);
+ }
assert(SymtabCmd.cmd && "No symbol table.");
uint64_t StringStart = SymtabStart + NumSyms * NListSize;
@@ -514,10 +534,9 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
// Reproduce that behavior for now (there is corresponding code in
// transferSymbol).
OutFile << '\0';
- std::vector<DwarfStringPoolEntryRef> Strings = NewStrings.getEntries();
+ std::vector<DwarfStringPoolEntryRef> Strings =
+ NewStrings.getEntriesForEmission();
for (auto EntryRef : Strings) {
- if (EntryRef.getIndex() == -1U)
- break;
OutFile.write(EntryRef.getString().data(),
EntryRef.getString().size() + 1);
}
diff --git a/tools/dsymutil/MachOUtils.h b/tools/dsymutil/MachOUtils.h
index a8be89e906b5..c24f963e1d98 100644
--- a/tools/dsymutil/MachOUtils.h
+++ b/tools/dsymutil/MachOUtils.h
@@ -9,8 +9,11 @@
#ifndef LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H
#define LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H
+#include "SymbolMap.h"
+
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FileSystem.h"
+
#include <string>
namespace llvm {
@@ -38,8 +41,8 @@ bool generateUniversalBinary(SmallVectorImpl<ArchAndFile> &ArchFiles,
StringRef OutputFileName, const LinkOptions &,
StringRef SDKPath);
-bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS,
- raw_fd_ostream &OutFile);
+bool generateDsymCompanion(const DebugMap &DM, SymbolMapTranslator &Translator,
+ MCStreamer &MS, raw_fd_ostream &OutFile);
std::string getArchName(StringRef Arch);
} // namespace MachOUtils
diff --git a/tools/dsymutil/NonRelocatableStringpool.cpp b/tools/dsymutil/NonRelocatableStringpool.cpp
index f1e5f596b849..b8392a112526 100644
--- a/tools/dsymutil/NonRelocatableStringpool.cpp
+++ b/tools/dsymutil/NonRelocatableStringpool.cpp
@@ -16,34 +16,40 @@ DwarfStringPoolEntryRef NonRelocatableStringpool::getEntry(StringRef S) {
if (S.empty() && !Strings.empty())
return EmptyString;
+ if (Translator)
+ S = Translator(S);
auto I = Strings.insert({S, DwarfStringPoolEntry()});
auto &Entry = I.first->second;
- if (I.second || Entry.Index == -1U) {
+ if (I.second || !Entry.isIndexed()) {
Entry.Index = NumEntries++;
Entry.Offset = CurrentEndOffset;
Entry.Symbol = nullptr;
CurrentEndOffset += S.size() + 1;
}
- return DwarfStringPoolEntryRef(*I.first);
+ return DwarfStringPoolEntryRef(*I.first, true);
}
StringRef NonRelocatableStringpool::internString(StringRef S) {
- DwarfStringPoolEntry Entry{nullptr, 0, -1U};
+ DwarfStringPoolEntry Entry{nullptr, 0, DwarfStringPoolEntry::NotIndexed};
+
+ if (Translator)
+ S = Translator(S);
+
auto InsertResult = Strings.insert({S, Entry});
return InsertResult.first->getKey();
}
std::vector<DwarfStringPoolEntryRef>
-NonRelocatableStringpool::getEntries() const {
+NonRelocatableStringpool::getEntriesForEmission() const {
std::vector<DwarfStringPoolEntryRef> Result;
Result.reserve(Strings.size());
for (const auto &E : Strings)
- Result.emplace_back(E);
- llvm::sort(
- Result.begin(), Result.end(),
- [](const DwarfStringPoolEntryRef A, const DwarfStringPoolEntryRef B) {
- return A.getIndex() < B.getIndex();
- });
+ if (E.getValue().isIndexed())
+ Result.emplace_back(E, true);
+ llvm::sort(Result, [](const DwarfStringPoolEntryRef A,
+ const DwarfStringPoolEntryRef B) {
+ return A.getIndex() < B.getIndex();
+ });
return Result;
}
diff --git a/tools/dsymutil/NonRelocatableStringpool.h b/tools/dsymutil/NonRelocatableStringpool.h
index 733ceebea614..c398ff0cec69 100644
--- a/tools/dsymutil/NonRelocatableStringpool.h
+++ b/tools/dsymutil/NonRelocatableStringpool.h
@@ -10,6 +10,8 @@
#ifndef LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H
#define LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H
+#include "SymbolMap.h"
+
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/CodeGen/DwarfStringPoolEntry.h"
@@ -32,7 +34,9 @@ public:
/// order.
using MapTy = StringMap<DwarfStringPoolEntry, BumpPtrAllocator>;
- NonRelocatableStringpool() {
+ NonRelocatableStringpool(
+ SymbolMapTranslator Translator = SymbolMapTranslator())
+ : Translator(Translator) {
// Legacy dsymutil puts an empty string at the start of the line table.
EmptyString = getEntry("");
}
@@ -53,13 +57,16 @@ public:
uint64_t getSize() { return CurrentEndOffset; }
- std::vector<DwarfStringPoolEntryRef> getEntries() const;
+ /// Return the list of strings to be emitted. This does not contain the
+ /// strings which were added via internString only.
+ std::vector<DwarfStringPoolEntryRef> getEntriesForEmission() const;
private:
MapTy Strings;
uint32_t CurrentEndOffset = 0;
unsigned NumEntries = 0;
DwarfStringPoolEntryRef EmptyString;
+ SymbolMapTranslator Translator;
};
/// Helper for making strong types.
diff --git a/tools/dsymutil/SymbolMap.cpp b/tools/dsymutil/SymbolMap.cpp
new file mode 100644
index 000000000000..cab9374a7d97
--- /dev/null
+++ b/tools/dsymutil/SymbolMap.cpp
@@ -0,0 +1,162 @@
+//===- tools/dsymutil/SymbolMap.cpp ---------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolMap.h"
+#include "DebugMap.h"
+#include "MachOUtils.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/WithColor.h"
+
+#ifdef __APPLE__
+#include <CoreFoundation/CoreFoundation.h>
+#include <uuid/uuid.h>
+#endif
+
+namespace llvm {
+namespace dsymutil {
+
+StringRef SymbolMapTranslator::operator()(StringRef Input) {
+ if (!Input.startswith("__hidden#") && !Input.startswith("___hidden#"))
+ return Input;
+
+ bool MightNeedUnderscore = false;
+ StringRef Line = Input.drop_front(sizeof("__hidden#") - 1);
+ if (Line[0] == '#') {
+ Line = Line.drop_front();
+ MightNeedUnderscore = true;
+ }
+
+ std::size_t LineNumber = std::numeric_limits<std::size_t>::max();
+ Line.split('_').first.getAsInteger(10, LineNumber);
+ if (LineNumber >= UnobfuscatedStrings.size()) {
+ WithColor::warning() << "reference to a unexisting unobfuscated string "
+ << Input << ": symbol map mismatch?\n"
+ << Line << '\n';
+ return Input;
+ }
+
+ const std::string &Translation = UnobfuscatedStrings[LineNumber];
+ if (!MightNeedUnderscore || !MangleNames)
+ return Translation;
+
+ // Objective-C symbols for the MachO symbol table start with a \1. Please see
+ // `CGObjCCommonMac::GetNameForMethod` in clang.
+ if (Translation[0] == 1)
+ return StringRef(Translation).drop_front();
+
+ // We need permanent storage for the string we are about to create. Just
+ // append it to the vector containing translations. This should only happen
+ // during MachO symbol table translation, thus there should be no risk on
+ // exponential growth.
+ UnobfuscatedStrings.emplace_back("_" + Translation);
+ return UnobfuscatedStrings.back();
+}
+
+SymbolMapTranslator SymbolMapLoader::Load(StringRef InputFile,
+ const DebugMap &Map) const {
+ if (SymbolMap.empty())
+ return {};
+
+ std::string SymbolMapPath = SymbolMap;
+
+#if __APPLE__
+ // Look through the UUID Map.
+ if (sys::fs::is_directory(SymbolMapPath) && !Map.getUUID().empty()) {
+ uuid_string_t UUIDString;
+ uuid_unparse_upper((const uint8_t *)Map.getUUID().data(), UUIDString);
+
+ SmallString<256> PlistPath(
+ sys::path::parent_path(sys::path::parent_path(InputFile)));
+ sys::path::append(PlistPath, StringRef(UUIDString).str() + ".plist");
+
+ CFStringRef plistFile = CFStringCreateWithCString(
+ kCFAllocatorDefault, PlistPath.c_str(), kCFStringEncodingUTF8);
+ CFURLRef fileURL = CFURLCreateWithFileSystemPath(
+ kCFAllocatorDefault, plistFile, kCFURLPOSIXPathStyle, false);
+ CFReadStreamRef resourceData =
+ CFReadStreamCreateWithFile(kCFAllocatorDefault, fileURL);
+ if (resourceData) {
+ CFReadStreamOpen(resourceData);
+ CFDictionaryRef plist = (CFDictionaryRef)CFPropertyListCreateWithStream(
+ kCFAllocatorDefault, resourceData, 0, kCFPropertyListImmutable,
+ nullptr, nullptr);
+
+ if (plist) {
+ if (CFDictionaryContainsKey(plist, CFSTR("DBGOriginalUUID"))) {
+ CFStringRef OldUUID = (CFStringRef)CFDictionaryGetValue(
+ plist, CFSTR("DBGOriginalUUID"));
+
+ StringRef UUID(CFStringGetCStringPtr(OldUUID, kCFStringEncodingUTF8));
+ SmallString<256> BCSymbolMapPath(SymbolMapPath);
+ sys::path::append(BCSymbolMapPath, UUID.str() + ".bcsymbolmap");
+ SymbolMapPath = BCSymbolMapPath.str();
+ }
+ CFRelease(plist);
+ }
+ CFReadStreamClose(resourceData);
+ CFRelease(resourceData);
+ }
+ CFRelease(fileURL);
+ CFRelease(plistFile);
+ }
+#endif
+
+ if (sys::fs::is_directory(SymbolMapPath)) {
+ SymbolMapPath += (Twine("/") + sys::path::filename(InputFile) + "-" +
+ MachOUtils::getArchName(Map.getTriple().getArchName()) +
+ ".bcsymbolmap")
+ .str();
+ }
+
+ auto ErrOrMemBuffer = MemoryBuffer::getFile(SymbolMapPath);
+ if (auto EC = ErrOrMemBuffer.getError()) {
+ WithColor::warning() << SymbolMapPath << ": " << EC.message()
+ << ": not unobfuscating.\n";
+ return {};
+ }
+
+ std::vector<std::string> UnobfuscatedStrings;
+ auto &MemBuf = **ErrOrMemBuffer;
+ StringRef Data(MemBuf.getBufferStart(),
+ MemBuf.getBufferEnd() - MemBuf.getBufferStart());
+ StringRef LHS;
+ std::tie(LHS, Data) = Data.split('\n');
+ bool MangleNames = false;
+
+ // Check version string first.
+ if (!LHS.startswith("BCSymbolMap Version:")) {
+ // Version string not present, warns but try to parse it.
+ WithColor::warning() << SymbolMapPath
+ << " is missing version string: assuming 1.0.\n";
+ UnobfuscatedStrings.emplace_back(LHS);
+ } else if (LHS.equals("BCSymbolMap Version: 1.0")) {
+ MangleNames = true;
+ } else if (LHS.equals("BCSymbolMap Version: 2.0")) {
+ MangleNames = false;
+ } else {
+ StringRef VersionNum;
+ std::tie(LHS, VersionNum) = LHS.split(':');
+ WithColor::warning() << SymbolMapPath
+ << " has unsupported symbol map version" << VersionNum
+ << ": not unobfuscating.\n";
+ return {};
+ }
+
+ while (!Data.empty()) {
+ std::tie(LHS, Data) = Data.split('\n');
+ UnobfuscatedStrings.emplace_back(LHS);
+ }
+
+ return SymbolMapTranslator(std::move(UnobfuscatedStrings), MangleNames);
+}
+
+} // namespace dsymutil
+} // namespace llvm
diff --git a/tools/dsymutil/SymbolMap.h b/tools/dsymutil/SymbolMap.h
new file mode 100644
index 000000000000..e3fbdbb01d82
--- /dev/null
+++ b/tools/dsymutil/SymbolMap.h
@@ -0,0 +1,54 @@
+//=- tools/dsymutil/SymbolMap.h -----------------------------------*- C++ -*-=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H
+#define LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H
+
+#include "llvm/ADT/StringRef.h"
+
+#include <string>
+#include <vector>
+
+namespace llvm {
+namespace dsymutil {
+class DebugMap;
+
+/// Callable class to unobfuscate strings based on a BCSymbolMap.
+class SymbolMapTranslator {
+public:
+ SymbolMapTranslator() : MangleNames(false) {}
+
+ SymbolMapTranslator(std::vector<std::string> UnobfuscatedStrings,
+ bool MangleNames)
+ : UnobfuscatedStrings(std::move(UnobfuscatedStrings)),
+ MangleNames(MangleNames) {}
+
+ StringRef operator()(StringRef Input);
+
+ operator bool() const { return !UnobfuscatedStrings.empty(); }
+
+private:
+ std::vector<std::string> UnobfuscatedStrings;
+ bool MangleNames;
+};
+
+/// Class to initialize SymbolMapTranslators from a BCSymbolMap.
+class SymbolMapLoader {
+public:
+ SymbolMapLoader(std::string SymbolMap) : SymbolMap(std::move(SymbolMap)) {}
+
+ SymbolMapTranslator Load(StringRef InputFile, const DebugMap &Map) const;
+
+private:
+ const std::string SymbolMap;
+};
+} // namespace dsymutil
+} // namespace llvm
+
+#endif // LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H
diff --git a/tools/dsymutil/dsymutil.cpp b/tools/dsymutil/dsymutil.cpp
index c0e6d505941f..ec8d0507b08a 100644
--- a/tools/dsymutil/dsymutil.cpp
+++ b/tools/dsymutil/dsymutil.cpp
@@ -7,9 +7,8 @@
//
//===----------------------------------------------------------------------===//
//
-// This program is a utility that aims to be a dropin replacement for
-// Darwin's dsymutil.
-//
+// This program is a utility that aims to be a dropin replacement for Darwin's
+// dsymutil.
//===----------------------------------------------------------------------===//
#include "dsymutil.h"
@@ -60,6 +59,8 @@ static opt<std::string>
OutputFileOpt("o",
desc("Specify the output file. default: <input file>.dwarf"),
value_desc("filename"), cat(DsymCategory));
+static alias OutputFileOptA("out", desc("Alias for -o"),
+ aliasopt(OutputFileOpt));
static opt<std::string> OsoPrependPath(
"oso-prepend-path",
@@ -101,6 +102,11 @@ static opt<bool> Update(
init(false), cat(DsymCategory));
static alias UpdateA("u", desc("Alias for --update"), aliasopt(Update));
+static opt<std::string> SymbolMap(
+ "symbol-map",
+ desc("Updates the existing dSYMs inplace using symbol map specified."),
+ value_desc("bcsymbolmap"), cat(DsymCategory));
+
static cl::opt<AccelTableKind> AcceleratorTable(
"accelerator", cl::desc("Output accelerator tables."),
cl::values(clEnumValN(AccelTableKind::Default, "Default",
@@ -165,20 +171,18 @@ static opt<bool>
desc("Embed warnings in the linked DWARF debug info."),
cat(DsymCategory));
-static bool createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) {
+static Error createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) {
if (NoOutput)
- return true;
+ return Error::success();
// Create plist file to write to.
llvm::SmallString<128> InfoPlist(BundleRoot);
llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
std::error_code EC;
llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text);
- if (EC) {
- WithColor::error() << "cannot create plist file " << InfoPlist << ": "
- << EC.message() << '\n';
- return false;
- }
+ if (EC)
+ return make_error<StringError>(
+ "cannot create Plist: " + toString(errorCodeToError(EC)), EC);
CFBundleInfo BI = getBundleInfo(Bin);
@@ -230,22 +234,21 @@ static bool createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) {
<< "</plist>\n";
PL.close();
- return true;
+ return Error::success();
}
-static bool createBundleDir(llvm::StringRef BundleBase) {
+static Error createBundleDir(llvm::StringRef BundleBase) {
if (NoOutput)
- return true;
+ return Error::success();
llvm::SmallString<128> Bundle(BundleBase);
llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF");
- if (std::error_code EC = create_directories(Bundle.str(), true,
- llvm::sys::fs::perms::all_all)) {
- WithColor::error() << "cannot create directory " << Bundle << ": "
- << EC.message() << "\n";
- return false;
- }
- return true;
+ if (std::error_code EC =
+ create_directories(Bundle.str(), true, llvm::sys::fs::perms::all_all))
+ return make_error<StringError>(
+ "cannot create bundle: " + toString(errorCodeToError(EC)), EC);
+
+ return Error::success();
}
static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) {
@@ -257,7 +260,7 @@ static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) {
Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);
if (!BinOrErr) {
- errs() << OutputFile << ": " << toString(BinOrErr.takeError());
+ WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError());
return false;
}
@@ -276,9 +279,12 @@ static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) {
return false;
}
-static std::string getOutputFileName(llvm::StringRef InputFile) {
+static Expected<std::string> getOutputFileName(llvm::StringRef InputFile) {
+ if (OutputFileOpt == "-")
+ return OutputFileOpt;
+
// When updating, do in place replacement.
- if (OutputFileOpt.empty() && Update)
+ if (OutputFileOpt.empty() && (Update || !SymbolMap.empty()))
return InputFile;
// If a flat dSYM has been requested, things are pretty simple.
@@ -305,8 +311,10 @@ static std::string getOutputFileName(llvm::StringRef InputFile) {
llvm::SmallString<128> BundleDir(OutputFileOpt);
if (BundleDir.empty())
BundleDir = DwarfFile + ".dSYM";
- if (!createBundleDir(BundleDir) || !createPlistFile(DwarfFile, BundleDir))
- return "";
+ if (auto E = createBundleDir(BundleDir))
+ return std::move(E);
+ if (auto E = createPlistFile(DwarfFile, BundleDir))
+ return std::move(E);
llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",
llvm::sys::path::filename(DwarfFile));
@@ -327,6 +335,9 @@ static Expected<LinkOptions> getOptions() {
Options.PrependPath = OsoPrependPath;
Options.TheAccelTableKind = AcceleratorTable;
+ if (!SymbolMap.empty())
+ Options.Update = true;
+
if (Assembly)
Options.FileType = OutputFileType::Assembly;
@@ -445,6 +456,13 @@ int main(int argc, char **argv) {
return 1;
}
+ if (InputFiles.size() > 1 && !SymbolMap.empty() &&
+ !llvm::sys::fs::is_directory(SymbolMap)) {
+ WithColor::error() << "when unobfuscating multiple files, --symbol-map "
+ << "needs to point to a directory.\n";
+ return 1;
+ }
+
if (getenv("RC_DEBUG_OPTIONS"))
PaperTrailWarnings = true;
@@ -459,6 +477,8 @@ int main(int argc, char **argv) {
return 1;
}
+ SymbolMapLoader SymMapLoader(SymbolMap);
+
for (auto &InputFile : *InputsOrErr) {
// Dump the symbol table for each input file and requested arch
if (DumpStab) {
@@ -513,6 +533,9 @@ int main(int argc, char **argv) {
if (DumpDebugMap)
continue;
+ if (!SymbolMap.empty())
+ OptionsOrErr->Translator = SymMapLoader.Load(InputFile, *Map);
+
if (Map->begin() == Map->end())
WithColor::warning()
<< "no debug symbols in executable (-arch "
@@ -521,13 +544,20 @@ int main(int argc, char **argv) {
// Using a std::shared_ptr rather than std::unique_ptr because move-only
// types don't work with std::bind in the ThreadPool implementation.
std::shared_ptr<raw_fd_ostream> OS;
- std::string OutputFile = getOutputFileName(InputFile);
+
+ Expected<std::string> OutputFileOrErr = getOutputFileName(InputFile);
+ if (!OutputFileOrErr) {
+ WithColor::error() << toString(OutputFileOrErr.takeError());
+ return 1;
+ }
+
+ std::string OutputFile = *OutputFileOrErr;
if (NeedsTempFiles) {
TempFiles.emplace_back(Map->getTriple().getArchName().str());
auto E = TempFiles.back().createTempFile();
if (E) {
- errs() << toString(std::move(E));
+ WithColor::error() << toString(std::move(E));
return 1;
}
@@ -540,7 +570,7 @@ int main(int argc, char **argv) {
OS = std::make_shared<raw_fd_ostream>(NoOutput ? "-" : OutputFile, EC,
sys::fs::F_None);
if (EC) {
- errs() << OutputFile << ": " << EC.message();
+ WithColor::error() << OutputFile << ": " << EC.message();
return 1;
}
}
@@ -567,10 +597,16 @@ int main(int argc, char **argv) {
if (!AllOK)
return 1;
- if (NeedsTempFiles &&
- !MachOUtils::generateUniversalBinary(
- TempFiles, getOutputFileName(InputFile), *OptionsOrErr, SDKPath))
- return 1;
+ if (NeedsTempFiles) {
+ Expected<std::string> OutputFileOrErr = getOutputFileName(InputFile);
+ if (!OutputFileOrErr) {
+ WithColor::error() << toString(OutputFileOrErr.takeError());
+ return 1;
+ }
+ if (!MachOUtils::generateUniversalBinary(TempFiles, *OutputFileOrErr,
+ *OptionsOrErr, SDKPath))
+ return 1;
+ }
}
return 0;
diff --git a/tools/gold/CMakeLists.txt b/tools/gold/CMakeLists.txt
index d2580329acab..72f76558c088 100644
--- a/tools/gold/CMakeLists.txt
+++ b/tools/gold/CMakeLists.txt
@@ -11,7 +11,7 @@ if( LLVM_ENABLE_PIC AND LLVM_BINUTILS_INCDIR )
IPO
)
- add_llvm_loadable_module(LLVMgold
+ add_llvm_library(LLVMgold MODULE
gold-plugin.cpp
)
diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp
index 6c55ebcddc43..738cafa6cacf 100644
--- a/tools/gold/gold-plugin.cpp
+++ b/tools/gold/gold-plugin.cpp
@@ -128,6 +128,7 @@ namespace options {
OT_NORMAL,
OT_DISABLE,
OT_BC_ONLY,
+ OT_ASM_ONLY,
OT_SAVE_TEMPS
};
static OutputType TheOutputType = OT_NORMAL;
@@ -229,6 +230,8 @@ namespace options {
TheOutputType = OT_SAVE_TEMPS;
} else if (opt == "disable-output") {
TheOutputType = OT_DISABLE;
+ } else if (opt == "emit-asm") {
+ TheOutputType = OT_ASM_ONLY;
} else if (opt == "thinlto") {
thinlto = true;
} else if (opt == "thinlto-index-only") {
@@ -447,8 +450,8 @@ static void diagnosticHandler(const DiagnosticInfo &DI) {
ld_plugin_level Level;
switch (DI.getSeverity()) {
case DS_Error:
- message(LDPL_FATAL, "LLVM gold plugin has failed to create LTO module: %s",
- ErrStorage.c_str());
+ Level = LDPL_FATAL;
+ break;
case DS_Warning:
Level = LDPL_WARNING;
break;
@@ -667,13 +670,9 @@ static void getThinLTOOldAndNewSuffix(std::string &OldSuffix,
/// suffix matching \p OldSuffix with \p NewSuffix.
static std::string getThinLTOObjectFileName(StringRef Path, StringRef OldSuffix,
StringRef NewSuffix) {
- if (OldSuffix.empty() && NewSuffix.empty())
- return Path;
- StringRef NewPath = Path;
- NewPath.consume_back(OldSuffix);
- std::string NewNewPath = NewPath;
- NewNewPath += NewSuffix;
- return NewNewPath;
+ if (Path.consume_back(OldSuffix))
+ return (Path + NewSuffix).str();
+ return Path;
}
// Returns true if S is valid as a C language identifier.
@@ -837,8 +836,10 @@ static std::unique_ptr<LTO> createLTO(IndexWriteCallback OnIndexWrite,
Conf.Options.RelaxELFRelocations = false;
// Toggle function/data sections.
- Conf.Options.FunctionSections = SplitSections;
- Conf.Options.DataSections = SplitSections;
+ if (FunctionSections.getNumOccurrences() == 0)
+ Conf.Options.FunctionSections = SplitSections;
+ if (DataSections.getNumOccurrences() == 0)
+ Conf.Options.DataSections = SplitSections;
Conf.MAttrs = MAttrs;
Conf.RelocModel = RelocationModel;
@@ -884,6 +885,9 @@ static std::unique_ptr<LTO> createLTO(IndexWriteCallback OnIndexWrite,
check(Conf.addSaveTemps(output_name + ".",
/* UseInputModulePath */ true));
break;
+ case options::OT_ASM_ONLY:
+ Conf.CGFileType = TargetMachine::CGFT_AssemblyFile;
+ break;
}
if (!options::sample_profile.empty())
@@ -1011,6 +1015,8 @@ static std::vector<std::pair<SmallString<128>, bool>> runLTO() {
Filename = options::obj_path;
else if (options::TheOutputType == options::OT_SAVE_TEMPS)
Filename = output_name + ".o";
+ else if (options::TheOutputType == options::OT_ASM_ONLY)
+ Filename = output_name;
bool SaveTemps = !Filename.empty();
size_t MaxTasks = Lto->getMaxTasks();
@@ -1059,7 +1065,8 @@ static ld_plugin_status allSymbolsReadHook() {
std::vector<std::pair<SmallString<128>, bool>> Files = runLTO();
if (options::TheOutputType == options::OT_DISABLE ||
- options::TheOutputType == options::OT_BC_ONLY)
+ options::TheOutputType == options::OT_BC_ONLY ||
+ options::TheOutputType == options::OT_ASM_ONLY)
return LDPS_OK;
if (options::thinlto_index_only) {
@@ -1084,6 +1091,7 @@ static ld_plugin_status all_symbols_read_hook(void) {
llvm_shutdown();
if (options::TheOutputType == options::OT_BC_ONLY ||
+ options::TheOutputType == options::OT_ASM_ONLY ||
options::TheOutputType == options::OT_DISABLE) {
if (options::TheOutputType == options::OT_DISABLE) {
// Remove the output file here since ld.bfd creates the output file
diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp
index 1940dbd848cc..7e93d31361aa 100644
--- a/tools/lli/lli.cpp
+++ b/tools/lli/lli.cpp
@@ -26,6 +26,7 @@
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/ExecutionEngine/ObjectCache.h"
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
+#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h"
#include "llvm/ExecutionEngine/OrcMCJITReplacement.h"
@@ -34,7 +35,6 @@
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
-#include "llvm/IR/TypeBuilder.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Object/Archive.h"
@@ -97,6 +97,28 @@ namespace {
"orc-lazy",
"Orc-based lazy JIT.")));
+ cl::opt<unsigned>
+ LazyJITCompileThreads("compile-threads",
+ cl::desc("Choose the number of compile threads "
+ "(jit-kind=orc-lazy only)"),
+ cl::init(0));
+
+ cl::list<std::string>
+ ThreadEntryPoints("thread-entry",
+ cl::desc("calls the given entry-point on a new thread "
+ "(jit-kind=orc-lazy only)"));
+
+ cl::opt<bool> PerModuleLazy(
+ "per-module-lazy",
+ cl::desc("Performs lazy compilation on whole module boundaries "
+ "rather than individual functions"),
+ cl::init(false));
+
+ cl::list<std::string>
+ JITDylibs("jd",
+ cl::desc("Specifies the JITDylib to be used for any subsequent "
+ "-extra-module arguments."));
+
// The MCJIT supports building for a target address space separate from
// the JIT compilation process. Use a forked process and a copying
// memory manager with IPC to execute using this functionality.
@@ -294,23 +316,18 @@ static void addCygMingExtraModule(ExecutionEngine &EE, LLVMContext &Context,
M->setTargetTriple(TargetTripleStr);
// Create an empty function named "__main".
- Function *Result;
- if (TargetTriple.isArch64Bit()) {
- Result = Function::Create(
- TypeBuilder<int64_t(void), false>::get(Context),
- GlobalValue::ExternalLinkage, "__main", M.get());
- } else {
- Result = Function::Create(
- TypeBuilder<int32_t(void), false>::get(Context),
- GlobalValue::ExternalLinkage, "__main", M.get());
- }
- BasicBlock *BB = BasicBlock::Create(Context, "__main", Result);
- Builder.SetInsertPoint(BB);
- Value *ReturnVal;
+ Type *ReturnTy;
if (TargetTriple.isArch64Bit())
- ReturnVal = ConstantInt::get(Context, APInt(64, 0));
+ ReturnTy = Type::getInt64Ty(Context);
else
- ReturnVal = ConstantInt::get(Context, APInt(32, 0));
+ ReturnTy = Type::getInt32Ty(Context);
+ Function *Result =
+ Function::Create(FunctionType::get(ReturnTy, {}, false),
+ GlobalValue::ExternalLinkage, "__main", M.get());
+
+ BasicBlock *BB = BasicBlock::Create(Context, "__main", Result);
+ Builder.SetInsertPoint(BB);
+ Value *ReturnVal = ConstantInt::get(ReturnTy, 0);
Builder.CreateRet(ReturnVal);
// Add this new module to the ExecutionEngine.
@@ -337,8 +354,8 @@ static void reportError(SMDiagnostic Err, const char *ProgName) {
exit(1);
}
-int runOrcLazyJIT(LLVMContext &Ctx, std::vector<std::unique_ptr<Module>> Ms,
- const std::vector<std::string> &Args);
+int runOrcLazyJIT(const char *ProgName);
+void disallowOrcOptions();
//===----------------------------------------------------------------------===//
// main Driver function
@@ -362,6 +379,11 @@ int main(int argc, char **argv, char * const *envp) {
if (DisableCoreFiles)
sys::Process::PreventCoreFiles();
+ if (UseJITKind == JITKind::OrcLazy)
+ return runOrcLazyJIT(argv[0]);
+ else
+ disallowOrcOptions();
+
LLVMContext Context;
// Load the bitcode...
@@ -371,21 +393,6 @@ int main(int argc, char **argv, char * const *envp) {
if (!Mod)
reportError(Err, argv[0]);
- if (UseJITKind == JITKind::OrcLazy) {
- std::vector<std::unique_ptr<Module>> Ms;
- Ms.push_back(std::move(Owner));
- for (auto &ExtraMod : ExtraModules) {
- Ms.push_back(parseIRFile(ExtraMod, Err, Context));
- if (!Ms.back())
- reportError(Err, argv[0]);
- }
- std::vector<std::string> Args;
- Args.push_back(InputFile);
- for (auto &Arg : InputArgv)
- Args.push_back(Arg);
- return runOrcLazyJIT(Context, std::move(Ms), Args);
- }
-
if (EnableCacheManager) {
std::string CacheName("file:");
CacheName.append(InputFile);
@@ -498,7 +505,7 @@ int main(int argc, char **argv, char * const *envp) {
if (!ArOrErr) {
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(ArOrErr.takeError(), OS, "");
+ logAllUnhandledErrors(ArOrErr.takeError(), OS);
OS.flush();
errs() << Buf;
exit(1);
@@ -688,16 +695,18 @@ int main(int argc, char **argv, char * const *envp) {
return Result;
}
-static orc::IRTransformLayer2::TransformFunction createDebugDumper() {
+static orc::IRTransformLayer::TransformFunction createDebugDumper() {
switch (OrcDumpKind) {
case DumpKind::NoDump:
- return [](std::unique_ptr<Module> M) { return M; };
+ return [](orc::ThreadSafeModule TSM,
+ const orc::MaterializationResponsibility &R) { return TSM; };
case DumpKind::DumpFuncsToStdOut:
- return [](std::unique_ptr<Module> M) {
+ return [](orc::ThreadSafeModule TSM,
+ const orc::MaterializationResponsibility &R) {
printf("[ ");
- for (const auto &F : *M) {
+ for (const auto &F : *TSM.getModule()) {
if (F.isDeclaration())
continue;
@@ -709,55 +718,58 @@ static orc::IRTransformLayer2::TransformFunction createDebugDumper() {
}
printf("]\n");
- return M;
+ return TSM;
};
case DumpKind::DumpModsToStdOut:
- return [](std::unique_ptr<Module> M) {
+ return [](orc::ThreadSafeModule TSM,
+ const orc::MaterializationResponsibility &R) {
outs() << "----- Module Start -----\n"
- << *M << "----- Module End -----\n";
+ << *TSM.getModule() << "----- Module End -----\n";
- return M;
+ return TSM;
};
case DumpKind::DumpModsToDisk:
- return [](std::unique_ptr<Module> M) {
+ return [](orc::ThreadSafeModule TSM,
+ const orc::MaterializationResponsibility &R) {
std::error_code EC;
- raw_fd_ostream Out(M->getModuleIdentifier() + ".ll", EC, sys::fs::F_Text);
+ raw_fd_ostream Out(TSM.getModule()->getModuleIdentifier() + ".ll", EC,
+ sys::fs::F_Text);
if (EC) {
- errs() << "Couldn't open " << M->getModuleIdentifier()
+ errs() << "Couldn't open " << TSM.getModule()->getModuleIdentifier()
<< " for dumping.\nError:" << EC.message() << "\n";
exit(1);
}
- Out << *M;
- return M;
+ Out << *TSM.getModule();
+ return TSM;
};
}
llvm_unreachable("Unknown DumpKind");
}
-int runOrcLazyJIT(LLVMContext &Ctx, std::vector<std::unique_ptr<Module>> Ms,
- const std::vector<std::string> &Args) {
- // Bail out early if no modules loaded.
- if (Ms.empty())
- return 0;
-
- // Add lli's symbols into the JIT's search space.
- std::string ErrMsg;
- sys::DynamicLibrary LibLLI =
- sys::DynamicLibrary::getPermanentLibrary(nullptr, &ErrMsg);
- if (!LibLLI.isValid()) {
- errs() << "Error loading lli symbols: " << ErrMsg << ".\n";
- return 1;
- }
+static void exitOnLazyCallThroughFailure() { exit(1); }
+
+int runOrcLazyJIT(const char *ProgName) {
+ // Start setting up the JIT environment.
- const auto &TT = Ms.front()->getTargetTriple();
- orc::JITTargetMachineBuilder TMD =
+ // Parse the main module.
+ orc::ThreadSafeContext TSCtx(llvm::make_unique<LLVMContext>());
+ SMDiagnostic Err;
+ auto MainModule = orc::ThreadSafeModule(
+ parseIRFile(InputFile, Err, *TSCtx.getContext()), TSCtx);
+ if (!MainModule)
+ reportError(Err, ProgName);
+
+ const auto &TT = MainModule.getModule()->getTargetTriple();
+ orc::JITTargetMachineBuilder JTMB =
TT.empty() ? ExitOnErr(orc::JITTargetMachineBuilder::detectHost())
: orc::JITTargetMachineBuilder(Triple(TT));
- TMD.setArch(MArch)
- .setCPU(getCPUStr())
+ if (!MArch.empty())
+ JTMB.getTargetTriple().setArchName(MArch);
+
+ JTMB.setCPU(getCPUStr())
.addFeatures(getFeatureList())
.setRelocationModel(RelocModel.getNumOccurrences()
? Optional<Reloc::Model>(RelocModel)
@@ -765,53 +777,135 @@ int runOrcLazyJIT(LLVMContext &Ctx, std::vector<std::unique_ptr<Module>> Ms,
.setCodeModel(CMModel.getNumOccurrences()
? Optional<CodeModel::Model>(CMModel)
: None);
- auto TM = ExitOnErr(TMD.createTargetMachine());
- auto DL = TM->createDataLayout();
- auto ES = llvm::make_unique<orc::ExecutionSession>();
- auto J =
- ExitOnErr(orc::LLLazyJIT::Create(std::move(ES), std::move(TM), DL, Ctx));
+
+ DataLayout DL = ExitOnErr(JTMB.getDefaultDataLayoutForTarget());
+
+ auto J = ExitOnErr(orc::LLLazyJIT::Create(
+ std::move(JTMB), DL,
+ pointerToJITTargetAddress(exitOnLazyCallThroughFailure),
+ LazyJITCompileThreads));
+
+ if (PerModuleLazy)
+ J->setPartitionFunction(orc::CompileOnDemandLayer::compileWholeModule);
auto Dump = createDebugDumper();
- J->setLazyCompileTransform(
- [&](std::unique_ptr<Module> M) {
- if (verifyModule(*M, &dbgs())) {
- dbgs() << "Bad module: " << *M << "\n";
- exit(1);
- }
- return Dump(std::move(M));
- });
- J->getMainVSO().setFallbackDefinitionGenerator(
- orc::DynamicLibraryFallbackGenerator(
- std::move(LibLLI), DL, [](orc::SymbolStringPtr) { return true; }));
+ J->setLazyCompileTransform([&](orc::ThreadSafeModule TSM,
+ const orc::MaterializationResponsibility &R) {
+ if (verifyModule(*TSM.getModule(), &dbgs())) {
+ dbgs() << "Bad module: " << *TSM.getModule() << "\n";
+ exit(1);
+ }
+ return Dump(std::move(TSM), R);
+ });
+ J->getMainJITDylib().setGenerator(
+ ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(DL)));
orc::MangleAndInterner Mangle(J->getExecutionSession(), DL);
- orc::LocalCXXRuntimeOverrides2 CXXRuntimeOverrides;
- ExitOnErr(CXXRuntimeOverrides.enable(J->getMainVSO(), Mangle));
+ orc::LocalCXXRuntimeOverrides CXXRuntimeOverrides;
+ ExitOnErr(CXXRuntimeOverrides.enable(J->getMainJITDylib(), Mangle));
+
+ // Add the main module.
+ ExitOnErr(J->addLazyIRModule(std::move(MainModule)));
+
+ // Create JITDylibs and add any extra modules.
+ {
+ // Create JITDylibs, keep a map from argument index to dylib. We will use
+ // -extra-module argument indexes to determine what dylib to use for each
+ // -extra-module.
+ std::map<unsigned, orc::JITDylib *> IdxToDylib;
+ IdxToDylib[0] = &J->getMainJITDylib();
+ for (auto JDItr = JITDylibs.begin(), JDEnd = JITDylibs.end();
+ JDItr != JDEnd; ++JDItr) {
+ IdxToDylib[JITDylibs.getPosition(JDItr - JITDylibs.begin())] =
+ &J->createJITDylib(*JDItr);
+ }
- for (auto &M : Ms) {
- orc::makeAllSymbolsExternallyAccessible(*M);
- ExitOnErr(J->addLazyIRModule(std::move(M)));
+ for (auto EMItr = ExtraModules.begin(), EMEnd = ExtraModules.end();
+ EMItr != EMEnd; ++EMItr) {
+ auto M = parseIRFile(*EMItr, Err, *TSCtx.getContext());
+ if (!M)
+ reportError(Err, ProgName);
+
+ auto EMIdx = ExtraModules.getPosition(EMItr - ExtraModules.begin());
+ assert(EMIdx != 0 && "ExtraModule should have index > 0");
+ auto JDItr = std::prev(IdxToDylib.lower_bound(EMIdx));
+ auto &JD = *JDItr->second;
+ ExitOnErr(
+ J->addLazyIRModule(JD, orc::ThreadSafeModule(std::move(M), TSCtx)));
+ }
}
+ // Add the objects.
+ for (auto &ObjPath : ExtraObjects) {
+ auto Obj = ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(ObjPath)));
+ ExitOnErr(J->addObjectFile(std::move(Obj)));
+ }
+
+ // Generate a argument string.
+ std::vector<std::string> Args;
+ Args.push_back(InputFile);
+ for (auto &Arg : InputArgv)
+ Args.push_back(Arg);
+
+ // Run any static constructors.
ExitOnErr(J->runConstructors());
+ // Run any -thread-entry points.
+ std::vector<std::thread> AltEntryThreads;
+ for (auto &ThreadEntryPoint : ThreadEntryPoints) {
+ auto EntryPointSym = ExitOnErr(J->lookup(ThreadEntryPoint));
+ typedef void (*EntryPointPtr)();
+ auto EntryPoint =
+ reinterpret_cast<EntryPointPtr>(static_cast<uintptr_t>(EntryPointSym.getAddress()));
+ AltEntryThreads.push_back(std::thread([EntryPoint]() { EntryPoint(); }));
+ }
+
+ J->getExecutionSession().dump(llvm::dbgs());
+
+ // Run main.
auto MainSym = ExitOnErr(J->lookup("main"));
typedef int (*MainFnPtr)(int, const char *[]);
std::vector<const char *> ArgV;
for (auto &Arg : Args)
ArgV.push_back(Arg.c_str());
+ ArgV.push_back(nullptr);
+
+ int ArgC = ArgV.size() - 1;
auto Main =
reinterpret_cast<MainFnPtr>(static_cast<uintptr_t>(MainSym.getAddress()));
- auto Result = Main(ArgV.size(), (const char **)ArgV.data());
+ auto Result = Main(ArgC, (const char **)ArgV.data());
- ExitOnErr(J->runDestructors());
+ // Wait for -entry-point threads.
+ for (auto &AltEntryThread : AltEntryThreads)
+ AltEntryThread.join();
+ // Run destructors.
+ ExitOnErr(J->runDestructors());
CXXRuntimeOverrides.runDestructors();
return Result;
}
+void disallowOrcOptions() {
+ // Make sure nobody used an orc-lazy specific option accidentally.
+
+ if (LazyJITCompileThreads != 0) {
+ errs() << "-compile-threads requires -jit-kind=orc-lazy\n";
+ exit(1);
+ }
+
+ if (!ThreadEntryPoints.empty()) {
+ errs() << "-thread-entry requires -jit-kind=orc-lazy\n";
+ exit(1);
+ }
+
+ if (PerModuleLazy) {
+ errs() << "-per-module-lazy requires -jit-kind=orc-lazy\n";
+ exit(1);
+ }
+}
+
std::unique_ptr<FDRawChannel> launchRemote() {
#ifndef LLVM_ON_UNIX
llvm_unreachable("launchRemote not supported on non-Unix platforms");
diff --git a/tools/llvm-ar/CMakeLists.txt b/tools/llvm-ar/CMakeLists.txt
index 2970a59beee2..191c684d5245 100644
--- a/tools/llvm-ar/CMakeLists.txt
+++ b/tools/llvm-ar/CMakeLists.txt
@@ -1,5 +1,6 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
+ BinaryFormat
Core
DlltoolDriver
LibDriver
diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp
index 9023bdd1a0d6..1c453ee0b569 100644
--- a/tools/llvm-ar/llvm-ar.cpp
+++ b/tools/llvm-ar/llvm-ar.cpp
@@ -24,6 +24,7 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MemoryBuffer.h"
@@ -32,6 +33,7 @@
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h"
#include "llvm/ToolDrivers/llvm-lib/LibDriver.h"
@@ -63,46 +65,45 @@ OPTIONS:
)";
const char ArHelp[] = R"(
-OVERVIEW: LLVM Archiver (llvm-ar)
+OVERVIEW: LLVM Archiver
- This program archives bitcode files into single libraries
-
-USAGE: llvm-ar [options] [relpos] [count] <archive-file> [members]...
+USAGE: llvm-ar [options] [-]<operation>[modifiers] [relpos] <archive> [files]
+ llvm-ar -M [<mri-script]
OPTIONS:
- -M -
- -format - Archive format to create
- =default - default
- =gnu - gnu
- =darwin - darwin
- =bsd - bsd
- -plugin=<string> - plugin (ignored for compatibility
- -help - Display available options
- -version - Display the version of this program
+ --format - Archive format to create
+ =default - default
+ =gnu - gnu
+ =darwin - darwin
+ =bsd - bsd
+ --plugin=<string> - Ignored for compatibility
+ --help - Display available options
+ --version - Display the version of this program
OPERATIONS:
- d[NsS] - delete file(s) from the archive
- m[abiSs] - move file(s) in the archive
- p[kN] - print file(s) found in the archive
- q[ufsS] - quick append file(s) to the archive
- r[abfiuRsS] - replace or insert file(s) into the archive
- t - display contents of archive
- x[No] - extract file(s) from the archive
-
-MODIFIERS (operation specific):
- [a] - put file(s) after [relpos]
- [b] - put file(s) before [relpos] (same as [i])
+ d - delete [files] from the archive
+ m - move [files] in the archive
+ p - print [files] found in the archive
+ q - quick append [files] to the archive
+ r - replace or insert [files] into the archive
+ s - act as ranlib
+ t - display contents of archive
+ x - extract [files] from the archive
+
+MODIFIERS:
+ [a] - put [files] after [relpos]
+ [b] - put [files] before [relpos] (same as [i])
+ [c] - do not warn if archive had to be created
[D] - use zero for timestamps and uids/gids (default)
- [i] - put file(s) before [relpos] (same as [b])
+ [i] - put [files] before [relpos] (same as [b])
+ [l] - ignored for compatibility
+ [L] - add archive's contents
[o] - preserve original dates
[s] - create an archive index (cf. ranlib)
[S] - do not build a symbol table
[T] - create a thin archive
- [u] - update only files newer than archive contents
+ [u] - update only [files] newer than archive contents
[U] - use actual timestamps and uids/gids
-
-MODIFIERS (generic):
- [c] - do not warn if the library had to be created
[v] - be verbose about actions taken
)";
@@ -115,7 +116,7 @@ void printHelpMessage() {
// Show the error message and exit.
LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) {
- errs() << ToolName << ": " << Error << ".\n";
+ WithColor::error(errs(), ToolName) << Error << ".\n";
printHelpMessage();
exit(1);
}
@@ -125,7 +126,7 @@ static void failIfError(std::error_code EC, Twine Context = "") {
return;
std::string ContextStr = Context.str();
- if (ContextStr == "")
+ if (ContextStr.empty())
fail(EC.message());
fail(Context + ": " + EC.message());
}
@@ -136,7 +137,7 @@ static void failIfError(Error E, Twine Context = "") {
handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {
std::string ContextStr = Context.str();
- if (ContextStr == "")
+ if (ContextStr.empty())
fail(EIB.message());
fail(Context + ": " + EIB.message());
});
@@ -157,14 +158,14 @@ static std::string Options;
// This enumeration delineates the kinds of operations on an archive
// that are permitted.
enum ArchiveOperation {
- Print, ///< Print the contents of the archive
- Delete, ///< Delete the specified members
- Move, ///< Move members to end or as given by {a,b,i} modifiers
- QuickAppend, ///< Quickly append to end of archive
- ReplaceOrInsert, ///< Replace or Insert members
- DisplayTable, ///< Display the table of contents
- Extract, ///< Extract files back to file system
- CreateSymTab ///< Create a symbol table in an existing archive
+ Print, ///< Print the contents of the archive
+ Delete, ///< Delete the specified members
+ Move, ///< Move members to end or as given by {a,b,i} modifiers
+ QuickAppend, ///< Quickly append to end of archive
+ ReplaceOrInsert, ///< Replace or Insert members
+ DisplayTable, ///< Display the table of contents
+ Extract, ///< Extract files back to file system
+ CreateSymTab ///< Create a symbol table in an existing archive
};
// Modifiers to follow operation to vary behavior
@@ -177,6 +178,7 @@ static bool Verbose = false; ///< 'v' modifier
static bool Symtab = true; ///< 's' modifier
static bool Deterministic = true; ///< 'D' and 'U' modifiers
static bool Thin = false; ///< 'T' modifier
+static bool AddLibrary = false; ///< 'L' modifier
// Relative Positional Argument (for insert/move). This variable holds
// the name of the archive member to which the 'a', 'b' or 'i' modifier
@@ -195,7 +197,7 @@ static std::vector<StringRef> Members;
// Extract the member filename from the command line for the [relpos] argument
// associated with a, b, and i modifiers
static void getRelPos() {
- if (PositionalArgs.size() == 0)
+ if (PositionalArgs.empty())
fail("Expected [relpos] for a, b, or i modifier");
RelPos = PositionalArgs[0];
PositionalArgs.erase(PositionalArgs.begin());
@@ -203,7 +205,7 @@ static void getRelPos() {
// Get the archive file name from the command line
static void getArchive() {
- if (PositionalArgs.size() == 0)
+ if (PositionalArgs.empty())
fail("An archive name must be specified");
ArchiveName = PositionalArgs[0];
PositionalArgs.erase(PositionalArgs.begin());
@@ -215,6 +217,21 @@ static void getMembers() {
Members.push_back(Arg);
}
+std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
+std::vector<std::unique_ptr<object::Archive>> Archives;
+
+static object::Archive &readLibrary(const Twine &Library) {
+ auto BufOrErr = MemoryBuffer::getFile(Library, -1, false);
+ failIfError(BufOrErr.getError(), "Could not open library " + Library);
+ ArchiveBuffers.push_back(std::move(*BufOrErr));
+ auto LibOrErr =
+ object::Archive::create(ArchiveBuffers.back()->getMemBufferRef());
+ failIfError(errorToErrorCode(LibOrErr.takeError()),
+ "Could not parse library");
+ Archives.push_back(std::move(*LibOrErr));
+ return *Archives.back();
+}
+
static void runMRIScript();
// Parse the command line options as presented and return the operation
@@ -240,18 +257,44 @@ static ArchiveOperation parseCommandLine() {
bool MaybeJustCreateSymTab = false;
- for(unsigned i=0; i<Options.size(); ++i) {
- switch(Options[i]) {
- case 'd': ++NumOperations; Operation = Delete; break;
- case 'm': ++NumOperations; Operation = Move ; break;
- case 'p': ++NumOperations; Operation = Print; break;
- case 'q': ++NumOperations; Operation = QuickAppend; break;
- case 'r': ++NumOperations; Operation = ReplaceOrInsert; break;
- case 't': ++NumOperations; Operation = DisplayTable; break;
- case 'x': ++NumOperations; Operation = Extract; break;
- case 'c': Create = true; break;
- case 'l': /* accepted but unused */ break;
- case 'o': OriginalDates = true; break;
+ for (unsigned i = 0; i < Options.size(); ++i) {
+ switch (Options[i]) {
+ case 'd':
+ ++NumOperations;
+ Operation = Delete;
+ break;
+ case 'm':
+ ++NumOperations;
+ Operation = Move;
+ break;
+ case 'p':
+ ++NumOperations;
+ Operation = Print;
+ break;
+ case 'q':
+ ++NumOperations;
+ Operation = QuickAppend;
+ break;
+ case 'r':
+ ++NumOperations;
+ Operation = ReplaceOrInsert;
+ break;
+ case 't':
+ ++NumOperations;
+ Operation = DisplayTable;
+ break;
+ case 'x':
+ ++NumOperations;
+ Operation = Extract;
+ break;
+ case 'c':
+ Create = true;
+ break;
+ case 'l': /* accepted but unused */
+ break;
+ case 'o':
+ OriginalDates = true;
+ break;
case 's':
Symtab = true;
MaybeJustCreateSymTab = true;
@@ -259,8 +302,12 @@ static ArchiveOperation parseCommandLine() {
case 'S':
Symtab = false;
break;
- case 'u': OnlyUpdate = true; break;
- case 'v': Verbose = true; break;
+ case 'u':
+ OnlyUpdate = true;
+ break;
+ case 'v':
+ Verbose = true;
+ break;
case 'a':
getRelPos();
AddAfter = true;
@@ -285,6 +332,9 @@ static ArchiveOperation parseCommandLine() {
case 'T':
Thin = true;
break;
+ case 'L':
+ AddLibrary = true;
+ break;
default:
fail(std::string("unknown option ") + Options[i]);
}
@@ -297,7 +347,7 @@ static ArchiveOperation parseCommandLine() {
// Everything on the command line at this point is a member.
getMembers();
- if (NumOperations == 0 && MaybeJustCreateSymTab) {
+ if (NumOperations == 0 && MaybeJustCreateSymTab) {
NumOperations = 1;
Operation = CreateSymTab;
if (!Members.empty())
@@ -321,6 +371,8 @@ static ArchiveOperation parseCommandLine() {
fail("The 'o' modifier is only applicable to the 'x' operation");
if (OnlyUpdate && Operation != ReplaceOrInsert)
fail("The 'u' modifier is only applicable to the 'r' operation");
+ if (AddLibrary && Operation != QuickAppend)
+ fail("The 'L' modifier is only applicable to the 'q' operation");
// Return the parsed operation to the caller
return Operation;
@@ -369,7 +421,11 @@ static void doDisplayTable(StringRef Name, const object::Archive::Child &C) {
outs() << ' ' << format("%6llu", Size.get());
auto ModTimeOrErr = C.getLastModified();
failIfError(ModTimeOrErr.takeError());
- outs() << ' ' << ModTimeOrErr.get();
+ // Note: formatv() only handles the default TimePoint<>, which is in
+ // nanoseconds.
+ // TODO: fix format_provider<TimePoint<>> to allow other units.
+ sys::TimePoint<> ModTimeInNs = ModTimeOrErr.get();
+ outs() << ' ' << formatv("{0:%b %e %H:%M %Y}", ModTimeInNs);
outs() << ' ';
}
@@ -412,7 +468,7 @@ static void doExtract(StringRef Name, const object::Archive::Child &C) {
auto ModTimeOrErr = C.getLastModified();
failIfError(ModTimeOrErr.takeError());
failIfError(
- sys::fs::setLastModificationAndAccessTime(FD, ModTimeOrErr.get()));
+ sys::fs::setLastAccessAndModificationTime(FD, ModTimeOrErr.get()));
}
if (close(FD))
@@ -477,36 +533,57 @@ static void performReadOperation(ArchiveOperation Operation,
if (Members.empty())
return;
for (StringRef Name : Members)
- errs() << Name << " was not found\n";
+ WithColor::error(errs(), ToolName) << "'" << Name << "' was not found\n";
exit(1);
}
+static void addChildMember(std::vector<NewArchiveMember> &Members,
+ const object::Archive::Child &M,
+ bool FlattenArchive = false) {
+ if (Thin && !M.getParent()->isThin())
+ fail("Cannot convert a regular archive to a thin one");
+ Expected<NewArchiveMember> NMOrErr =
+ NewArchiveMember::getOldMember(M, Deterministic);
+ failIfError(NMOrErr.takeError());
+ if (FlattenArchive &&
+ identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) {
+ Expected<std::string> FileNameOrErr = M.getFullName();
+ failIfError(FileNameOrErr.takeError());
+ object::Archive &Lib = readLibrary(*FileNameOrErr);
+ // When creating thin archives, only flatten if the member is also thin.
+ if (!Thin || Lib.isThin()) {
+ Error Err = Error::success();
+ // Only Thin archives are recursively flattened.
+ for (auto &Child : Lib.children(Err))
+ addChildMember(Members, Child, /*FlattenArchive=*/Thin);
+ failIfError(std::move(Err));
+ return;
+ }
+ }
+ Members.push_back(std::move(*NMOrErr));
+}
+
static void addMember(std::vector<NewArchiveMember> &Members,
- StringRef FileName, int Pos = -1) {
+ StringRef FileName, bool FlattenArchive = false) {
Expected<NewArchiveMember> NMOrErr =
NewArchiveMember::getFile(FileName, Deterministic);
failIfError(NMOrErr.takeError(), FileName);
-
+ if (FlattenArchive &&
+ identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) {
+ object::Archive &Lib = readLibrary(FileName);
+ // When creating thin archives, only flatten if the member is also thin.
+ if (!Thin || Lib.isThin()) {
+ Error Err = Error::success();
+ // Only Thin archives are recursively flattened.
+ for (auto &Child : Lib.children(Err))
+ addChildMember(Members, Child, /*FlattenArchive=*/Thin);
+ failIfError(std::move(Err));
+ return;
+ }
+ }
// Use the basename of the object path for the member name.
NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName);
-
- if (Pos == -1)
- Members.push_back(std::move(*NMOrErr));
- else
- Members[Pos] = std::move(*NMOrErr);
-}
-
-static void addMember(std::vector<NewArchiveMember> &Members,
- const object::Archive::Child &M, int Pos = -1) {
- if (Thin && !M.getParent()->isThin())
- fail("Cannot convert a regular archive to a thin one");
- Expected<NewArchiveMember> NMOrErr =
- NewArchiveMember::getOldMember(M, Deterministic);
- failIfError(NMOrErr.takeError());
- if (Pos == -1)
- Members.push_back(std::move(*NMOrErr));
- else
- Members[Pos] = std::move(*NMOrErr);
+ Members.push_back(std::move(*NMOrErr));
}
enum InsertAction {
@@ -595,7 +672,7 @@ computeNewArchiveMembers(ArchiveOperation Operation,
computeInsertAction(Operation, Child, Name, MemberI);
switch (Action) {
case IA_AddOldMember:
- addMember(Ret, Child);
+ addChildMember(Ret, Child);
break;
case IA_AddNewMember:
addMember(Ret, *MemberI);
@@ -603,7 +680,7 @@ computeNewArchiveMembers(ArchiveOperation Operation,
case IA_Delete:
break;
case IA_MoveOldMember:
- addMember(Moved, Child);
+ addChildMember(Moved, Child);
break;
case IA_MoveNewMember:
addMember(Moved, *MemberI);
@@ -631,14 +708,20 @@ computeNewArchiveMembers(ArchiveOperation Operation,
++Pos;
}
- for (unsigned I = 0; I != Members.size(); ++I)
- Ret.insert(Ret.begin() + InsertPos, NewArchiveMember());
- Pos = InsertPos;
- for (auto &Member : Members) {
- addMember(Ret, Member, Pos);
- ++Pos;
+ if (AddLibrary) {
+ assert(Operation == QuickAppend);
+ for (auto &Member : Members)
+ addMember(Ret, Member, /*FlattenArchive=*/true);
+ return Ret;
}
+ std::vector<NewArchiveMember> NewMembers;
+ for (auto &Member : Members)
+ addMember(NewMembers, Member, /*FlattenArchive=*/Thin);
+ Ret.reserve(Ret.size() + NewMembers.size());
+ std::move(NewMembers.begin(), NewMembers.end(),
+ std::inserter(Ret, std::next(Ret.begin(), InsertPos)));
+
return Ret;
}
@@ -662,11 +745,10 @@ static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) {
return getDefaultForHost();
}
-static void
-performWriteOperation(ArchiveOperation Operation,
- object::Archive *OldArchive,
- std::unique_ptr<MemoryBuffer> OldArchiveBuf,
- std::vector<NewArchiveMember> *NewMembersP) {
+static void performWriteOperation(ArchiveOperation Operation,
+ object::Archive *OldArchive,
+ std::unique_ptr<MemoryBuffer> OldArchiveBuf,
+ std::vector<NewArchiveMember> *NewMembersP) {
std::vector<NewArchiveMember> NewMembers;
if (!NewMembersP)
NewMembers = computeNewArchiveMembers(Operation, OldArchive);
@@ -679,11 +761,11 @@ performWriteOperation(ArchiveOperation Operation,
else if (OldArchive)
Kind = OldArchive->kind();
else if (NewMembersP)
- Kind = NewMembersP->size() ? getKindFromMember(NewMembersP->front())
- : getDefaultForHost();
+ Kind = !NewMembersP->empty() ? getKindFromMember(NewMembersP->front())
+ : getDefaultForHost();
else
- Kind = NewMembers.size() ? getKindFromMember(NewMembers.front())
- : getDefaultForHost();
+ Kind = !NewMembers.empty() ? getKindFromMember(NewMembers.front())
+ : getDefaultForHost();
break;
case GNU:
Kind = object::Archive::K_GNU;
@@ -772,7 +854,8 @@ static int performOperation(ArchiveOperation Operation,
} else {
if (!Create) {
// Produce a warning if we should and we're creating the archive
- errs() << ToolName << ": creating " << ArchiveName << "\n";
+ WithColor::warning(errs(), ToolName)
+ << "creating " << ArchiveName << "\n";
}
}
@@ -788,11 +871,14 @@ static void runMRIScript() {
const MemoryBuffer &Ref = *Buf.get();
bool Saved = false;
std::vector<NewArchiveMember> NewMembers;
- std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
- std::vector<std::unique_ptr<object::Archive>> Archives;
- for (line_iterator I(Ref, /*SkipBlanks*/ true, ';'), E; I != E; ++I) {
+ for (line_iterator I(Ref, /*SkipBlanks*/ false), E; I != E; ++I) {
StringRef Line = *I;
+ Line = Line.split(';').first;
+ Line = Line.split('*').first;
+ Line = Line.trim();
+ if (Line.empty())
+ continue;
StringRef CommandStr, Rest;
std::tie(CommandStr, Rest) = Line.split(' ');
Rest = Rest.trim();
@@ -809,19 +895,11 @@ static void runMRIScript() {
switch (Command) {
case MRICommand::AddLib: {
- auto BufOrErr = MemoryBuffer::getFile(Rest, -1, false);
- failIfError(BufOrErr.getError(), "Could not open library");
- ArchiveBuffers.push_back(std::move(*BufOrErr));
- auto LibOrErr =
- object::Archive::create(ArchiveBuffers.back()->getMemBufferRef());
- failIfError(errorToErrorCode(LibOrErr.takeError()),
- "Could not parse library");
- Archives.push_back(std::move(*LibOrErr));
- object::Archive &Lib = *Archives.back();
+ object::Archive &Lib = readLibrary(Rest);
{
Error Err = Error::success();
for (auto &Member : Lib.children(Err))
- addMember(NewMembers, Member);
+ addChildMember(NewMembers, Member);
failIfError(std::move(Err));
}
break;
@@ -876,7 +954,7 @@ static int ar_main(int argc, char **argv) {
BumpPtrAllocator Alloc;
StringSaver Saver(Alloc);
cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv);
- for(size_t i = 1; i < Argv.size(); ++i) {
+ for (size_t i = 1; i < Argv.size(); ++i) {
StringRef Arg = Argv[i];
const char *match;
auto MatchFlagWithArg = [&](const char *expected) {
@@ -887,8 +965,7 @@ static int ar_main(int argc, char **argv) {
match = Argv[i];
return true;
}
- if (Arg.startswith(expected) && Arg.size() > len &&
- Arg[len] == '=') {
+ if (Arg.startswith(expected) && Arg.size() > len && Arg[len] == '=') {
match = Arg.data() + len + 1;
return true;
}
@@ -897,7 +974,7 @@ static int ar_main(int argc, char **argv) {
if (handleGenericOption(Argv[i]))
return 0;
if (Arg == "--") {
- for(; i < Argv.size(); ++i)
+ for (; i < Argv.size(); ++i)
PositionalArgs.push_back(Argv[i]);
break;
}
@@ -910,11 +987,11 @@ static int ar_main(int argc, char **argv) {
MRI = true;
} else if (MatchFlagWithArg("format")) {
FormatType = StringSwitch<Format>(match)
- .Case("default", Default)
- .Case("gnu", GNU)
- .Case("darwin", DARWIN)
- .Case("bsd", BSD)
- .Default(Unknown);
+ .Case("default", Default)
+ .Case("gnu", GNU)
+ .Case("darwin", DARWIN)
+ .Case("bsd", BSD)
+ .Default(Unknown);
if (FormatType == Unknown)
fail(std::string("Invalid format ") + match);
} else if (MatchFlagWithArg("plugin")) {
@@ -934,7 +1011,7 @@ static int ar_main(int argc, char **argv) {
static int ranlib_main(int argc, char **argv) {
bool ArchiveSpecified = false;
- for(int i = 1; i < argc; ++i) {
+ for (int i = 1; i < argc; ++i) {
if (handleGenericOption(argv[i])) {
return 0;
} else {
diff --git a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
index 1939dc6440fe..789a666cb41a 100644
--- a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
+++ b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
@@ -247,6 +247,7 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(CST_CODE, CE_CMP)
STRINGIFY_CODE(CST_CODE, INLINEASM)
STRINGIFY_CODE(CST_CODE, CE_SHUFVEC_EX)
+ STRINGIFY_CODE(CST_CODE, CE_UNOP)
case bitc::CST_CODE_BLOCKADDRESS: return "CST_CODE_BLOCKADDRESS";
STRINGIFY_CODE(CST_CODE, DATA)
}
@@ -267,6 +268,7 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(FUNC_CODE, INST_BR)
STRINGIFY_CODE(FUNC_CODE, INST_SWITCH)
STRINGIFY_CODE(FUNC_CODE, INST_INVOKE)
+ STRINGIFY_CODE(FUNC_CODE, INST_UNOP)
STRINGIFY_CODE(FUNC_CODE, INST_UNREACHABLE)
STRINGIFY_CODE(FUNC_CODE, INST_CLEANUPRET)
STRINGIFY_CODE(FUNC_CODE, INST_CATCHRET)
@@ -285,6 +287,11 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(FUNC_CODE, DEBUG_LOC)
STRINGIFY_CODE(FUNC_CODE, INST_GEP)
STRINGIFY_CODE(FUNC_CODE, OPERAND_BUNDLE)
+ STRINGIFY_CODE(FUNC_CODE, INST_FENCE)
+ STRINGIFY_CODE(FUNC_CODE, INST_ATOMICRMW)
+ STRINGIFY_CODE(FUNC_CODE, INST_LOADATOMIC)
+ STRINGIFY_CODE(FUNC_CODE, INST_STOREATOMIC)
+ STRINGIFY_CODE(FUNC_CODE, INST_CMPXCHG)
}
case bitc::VALUE_SYMTAB_BLOCK_ID:
switch (CodeID) {
diff --git a/tools/llvm-c-test/debuginfo.c b/tools/llvm-c-test/debuginfo.c
index 74d215ea8182..fbd41e97d7a1 100644
--- a/tools/llvm-c-test/debuginfo.c
+++ b/tools/llvm-c-test/debuginfo.c
@@ -61,26 +61,24 @@ int llvm_test_dibuilder(void) {
LLVMMetadataRef ClassTy = declare_objc_class(DIB, File);
LLVMMetadataRef GlobalClassValueExpr =
- LLVMDIBuilderCreateConstantValueExpression(DIB, 0);
- LLVMDIBuilderCreateGlobalVariableExpression(DIB, Module, "globalClass", 11,
- "", 0, File, 1, ClassTy,
- true, GlobalClassValueExpr,
- NULL, 0);
+ LLVMDIBuilderCreateConstantValueExpression(DIB, 0);
+ LLVMDIBuilderCreateGlobalVariableExpression(
+ DIB, Module, "globalClass", 11, "", 0, File, 1, ClassTy, true,
+ GlobalClassValueExpr, NULL, 0);
LLVMMetadataRef Int64Ty =
- LLVMDIBuilderCreateBasicType(DIB, "Int64", 5, 64, 0);
+ LLVMDIBuilderCreateBasicType(DIB, "Int64", 5, 64, 0, LLVMDIFlagZero);
LLVMMetadataRef Int64TypeDef =
LLVMDIBuilderCreateTypedef(DIB, Int64Ty, "int64_t", 7, File, 42, File);
LLVMMetadataRef GlobalVarValueExpr =
- LLVMDIBuilderCreateConstantValueExpression(DIB, 0);
- LLVMDIBuilderCreateGlobalVariableExpression(DIB, Module, "global", 6,
- "", 0, File, 1, Int64TypeDef,
- true, GlobalVarValueExpr,
- NULL, 0);
+ LLVMDIBuilderCreateConstantValueExpression(DIB, 0);
+ LLVMDIBuilderCreateGlobalVariableExpression(
+ DIB, Module, "global", 6, "", 0, File, 1, Int64TypeDef, true,
+ GlobalVarValueExpr, NULL, 0);
LLVMMetadataRef NameSpace =
- LLVMDIBuilderCreateNameSpace(DIB, Module, "NameSpace", 9, false);
+ LLVMDIBuilderCreateNameSpace(DIB, Module, "NameSpace", 9, false);
LLVMMetadataRef StructDbgElts[] = {Int64Ty, Int64Ty, Int64Ty};
LLVMMetadataRef StructDbgTy =
diff --git a/tools/llvm-c-test/echo.cpp b/tools/llvm-c-test/echo.cpp
index a9cd4cf5cbb7..db926e8aceaa 100644
--- a/tools/llvm-c-test/echo.cpp
+++ b/tools/llvm-c-test/echo.cpp
@@ -16,6 +16,7 @@
//===----------------------------------------------------------------------===//
#include "llvm-c-test.h"
+#include "llvm-c/DebugInfo.h"
#include "llvm-c/Target.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/ErrorHandling.h"
@@ -239,7 +240,17 @@ static LLVMValueRef clone_constant_impl(LLVMValueRef Cst, LLVMModuleRef M) {
// Try function
if (LLVMIsAFunction(Cst)) {
check_value_kind(Cst, LLVMFunctionValueKind);
- LLVMValueRef Dst = LLVMGetNamedFunction(M, Name);
+
+ LLVMValueRef Dst = nullptr;
+ // Try an intrinsic
+ unsigned ID = LLVMGetIntrinsicID(Cst);
+ if (ID > 0 && !LLVMIntrinsicIsOverloaded(ID)) {
+ Dst = LLVMGetIntrinsicDeclaration(M, ID, nullptr, 0);
+ } else {
+ // Try a normal function
+ Dst = LLVMGetNamedFunction(M, Name);
+ }
+
if (Dst)
return Dst;
report_fatal_error("Could not find function");
@@ -728,6 +739,19 @@ struct FunCloner {
exit(-1);
}
+ auto Ctx = LLVMGetModuleContext(M);
+ size_t NumMetadataEntries;
+ auto *AllMetadata =
+ LLVMInstructionGetAllMetadataOtherThanDebugLoc(Src,
+ &NumMetadataEntries);
+ for (unsigned i = 0; i < NumMetadataEntries; ++i) {
+ unsigned Kind = LLVMValueMetadataEntriesGetKind(AllMetadata, i);
+ LLVMMetadataRef MD = LLVMValueMetadataEntriesGetMetadata(AllMetadata, i);
+ LLVMSetMetadata(Dst, Kind, LLVMMetadataAsValue(Ctx, MD));
+ }
+ LLVMDisposeValueMetadataEntries(AllMetadata);
+ LLVMSetInstDebugLocation(Builder, Dst);
+
check_value_kind(Dst, LLVMInstructionValueKind);
return VMap[Src] = Dst;
}
@@ -916,7 +940,7 @@ AliasDecl:
if (!Begin) {
if (End != nullptr)
report_fatal_error("Range has an end but no beginning");
- return;
+ goto NamedMDDecl;
}
Cur = Begin;
@@ -943,6 +967,38 @@ AliasDecl:
Cur = Next;
}
+
+NamedMDDecl:
+ LLVMNamedMDNodeRef BeginMD = LLVMGetFirstNamedMetadata(Src);
+ LLVMNamedMDNodeRef EndMD = LLVMGetLastNamedMetadata(Src);
+ if (!BeginMD) {
+ if (EndMD != nullptr)
+ report_fatal_error("Range has an end but no beginning");
+ return;
+ }
+
+ LLVMNamedMDNodeRef CurMD = BeginMD;
+ LLVMNamedMDNodeRef NextMD = nullptr;
+ while (true) {
+ size_t NameLen;
+ const char *Name = LLVMGetNamedMetadataName(CurMD, &NameLen);
+ if (LLVMGetNamedMetadata(M, Name, NameLen))
+ report_fatal_error("Named Metadata Node already cloned");
+ LLVMGetOrInsertNamedMetadata(M, Name, NameLen);
+
+ NextMD = LLVMGetNextNamedMetadata(CurMD);
+ if (NextMD == nullptr) {
+ if (CurMD != EndMD)
+ report_fatal_error("");
+ break;
+ }
+
+ LLVMNamedMDNodeRef PrevMD = LLVMGetPreviousNamedMetadata(NextMD);
+ if (PrevMD != CurMD)
+ report_fatal_error("Next.Previous global is not Current");
+
+ CurMD = NextMD;
+ }
}
static void clone_symbols(LLVMModuleRef Src, LLVMModuleRef M) {
@@ -967,6 +1023,15 @@ static void clone_symbols(LLVMModuleRef Src, LLVMModuleRef M) {
if (auto I = LLVMGetInitializer(Cur))
LLVMSetInitializer(G, clone_constant(I, M));
+ size_t NumMetadataEntries;
+ auto *AllMetadata = LLVMGlobalCopyAllMetadata(Cur, &NumMetadataEntries);
+ for (unsigned i = 0; i < NumMetadataEntries; ++i) {
+ unsigned Kind = LLVMValueMetadataEntriesGetKind(AllMetadata, i);
+ LLVMMetadataRef MD = LLVMValueMetadataEntriesGetMetadata(AllMetadata, i);
+ LLVMGlobalSetMetadata(G, Kind, MD);
+ }
+ LLVMDisposeValueMetadataEntries(AllMetadata);
+
LLVMSetGlobalConstant(G, LLVMIsGlobalConstant(Cur));
LLVMSetThreadLocal(G, LLVMIsThreadLocal(Cur));
LLVMSetExternallyInitialized(G, LLVMIsExternallyInitialized(Cur));
@@ -1018,6 +1083,15 @@ FunClone:
LLVMSetPersonalityFn(Fun, P);
}
+ size_t NumMetadataEntries;
+ auto *AllMetadata = LLVMGlobalCopyAllMetadata(Cur, &NumMetadataEntries);
+ for (unsigned i = 0; i < NumMetadataEntries; ++i) {
+ unsigned Kind = LLVMValueMetadataEntriesGetKind(AllMetadata, i);
+ LLVMMetadataRef MD = LLVMValueMetadataEntriesGetMetadata(AllMetadata, i);
+ LLVMGlobalSetMetadata(Fun, Kind, MD);
+ }
+ LLVMDisposeValueMetadataEntries(AllMetadata);
+
FunCloner FC(Cur, Fun);
FC.CloneBBs(Cur);
@@ -1041,7 +1115,7 @@ AliasClone:
if (!Begin) {
if (End != nullptr)
report_fatal_error("Range has an end but no beginning");
- return;
+ goto NamedMDClone;
}
Cur = Begin;
@@ -1073,6 +1147,47 @@ AliasClone:
Cur = Next;
}
+
+NamedMDClone:
+ LLVMNamedMDNodeRef BeginMD = LLVMGetFirstNamedMetadata(Src);
+ LLVMNamedMDNodeRef EndMD = LLVMGetLastNamedMetadata(Src);
+ if (!BeginMD) {
+ if (EndMD != nullptr)
+ report_fatal_error("Range has an end but no beginning");
+ return;
+ }
+
+ LLVMNamedMDNodeRef CurMD = BeginMD;
+ LLVMNamedMDNodeRef NextMD = nullptr;
+ while (true) {
+ size_t NameLen;
+ const char *Name = LLVMGetNamedMetadataName(CurMD, &NameLen);
+ LLVMNamedMDNodeRef NamedMD = LLVMGetNamedMetadata(M, Name, NameLen);
+ if (!NamedMD)
+ report_fatal_error("Named MD Node must have been declared already");
+
+ unsigned OperandCount = LLVMGetNamedMetadataNumOperands(Src, Name);
+ LLVMValueRef *OperandBuf = static_cast<LLVMValueRef *>(
+ safe_malloc(OperandCount * sizeof(LLVMValueRef)));
+ LLVMGetNamedMetadataOperands(Src, Name, OperandBuf);
+ for (unsigned i = 0, e = OperandCount; i != e; ++i) {
+ LLVMAddNamedMetadataOperand(M, Name, OperandBuf[i]);
+ }
+ free(OperandBuf);
+
+ NextMD = LLVMGetNextNamedMetadata(CurMD);
+ if (NextMD == nullptr) {
+ if (CurMD != EndMD)
+ report_fatal_error("Last Named MD Node does not match End");
+ break;
+ }
+
+ LLVMNamedMDNodeRef PrevMD = LLVMGetPreviousNamedMetadata(NextMD);
+ if (PrevMD != CurMD)
+ report_fatal_error("Next.Previous Named MD Node is not Current");
+
+ CurMD = NextMD;
+ }
}
int llvm_echo(void) {
@@ -1089,18 +1204,6 @@ int llvm_echo(void) {
LLVMSetSourceFileName(M, SourceFileName, SourceFileLen);
LLVMSetModuleIdentifier(M, ModuleName, ModuleIdentLen);
- size_t SourceFlagsLen;
- LLVMModuleFlagEntry *ModuleFlags =
- LLVMCopyModuleFlagsMetadata(Src, &SourceFlagsLen);
- for (unsigned i = 0; i < SourceFlagsLen; ++i) {
- size_t EntryNameLen;
- const char *EntryName =
- LLVMModuleFlagEntriesGetKey(ModuleFlags, i, &EntryNameLen);
- LLVMAddModuleFlag(M, LLVMModuleFlagEntriesGetFlagBehavior(ModuleFlags, i),
- EntryName, EntryNameLen,
- LLVMModuleFlagEntriesGetMetadata(ModuleFlags, i));
- }
-
LLVMSetTarget(M, LLVMGetTarget(Src));
LLVMSetModuleDataLayout(M, LLVMGetModuleDataLayout(Src));
if (strcmp(LLVMGetDataLayoutStr(M), LLVMGetDataLayoutStr(Src)))
@@ -1115,7 +1218,6 @@ int llvm_echo(void) {
char *Str = LLVMPrintModuleToString(M);
fputs(Str, stdout);
- LLVMDisposeModuleFlagsMetadata(ModuleFlags);
LLVMDisposeMessage(Str);
LLVMDisposeModule(Src);
LLVMDisposeModule(M);
diff --git a/tools/llvm-cfi-verify/lib/FileAnalysis.cpp b/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
index 29819d8d28ed..8dc0802c4652 100644
--- a/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
+++ b/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
@@ -105,6 +105,9 @@ Expected<FileAnalysis> FileAnalysis::Create(StringRef Filename) {
if (auto SectionParseResponse = Analysis.parseCodeSections())
return std::move(SectionParseResponse);
+ if (auto SymbolTableParseResponse = Analysis.parseSymbolTable())
+ return std::move(SymbolTableParseResponse);
+
return std::move(Analysis);
}
@@ -165,7 +168,18 @@ const Instr &FileAnalysis::getInstructionOrDie(uint64_t Address) const {
bool FileAnalysis::isCFITrap(const Instr &InstrMeta) const {
const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
- return InstrDesc.isTrap();
+ return InstrDesc.isTrap() || willTrapOnCFIViolation(InstrMeta);
+}
+
+bool FileAnalysis::willTrapOnCFIViolation(const Instr &InstrMeta) const {
+ const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
+ if (!InstrDesc.isCall())
+ return false;
+ uint64_t Target;
+ if (!MIA->evaluateBranch(InstrMeta.Instruction, InstrMeta.VMAddress,
+ InstrMeta.InstructionSize, Target))
+ return false;
+ return TrapOnFailFunctionAddresses.count(Target) > 0;
}
bool FileAnalysis::canFallThrough(const Instr &InstrMeta) const {
@@ -431,6 +445,12 @@ Error FileAnalysis::parseCodeSections() {
if (!(object::ELFSectionRef(Section).getFlags() & ELF::SHF_EXECINSTR))
continue;
+ // Avoid checking the PLT since it produces spurious failures on AArch64
+ // when ignoring DWARF data.
+ StringRef SectionName;
+ if (!Section.getName(SectionName) && SectionName == ".plt")
+ continue;
+
StringRef SectionContents;
if (Section.getContents(SectionContents))
return make_error<StringError>("Failed to retrieve section contents",
@@ -518,6 +538,40 @@ void FileAnalysis::addInstruction(const Instr &Instruction) {
}
}
+Error FileAnalysis::parseSymbolTable() {
+ // Functions that will trap on CFI violations.
+ SmallSet<StringRef, 4> TrapOnFailFunctions;
+ TrapOnFailFunctions.insert("__cfi_slowpath");
+ TrapOnFailFunctions.insert("__cfi_slowpath_diag");
+ TrapOnFailFunctions.insert("abort");
+
+ // Look through the list of symbols for functions that will trap on CFI
+ // violations.
+ for (auto &Sym : Object->symbols()) {
+ auto SymNameOrErr = Sym.getName();
+ if (!SymNameOrErr)
+ consumeError(SymNameOrErr.takeError());
+ else if (TrapOnFailFunctions.count(*SymNameOrErr) > 0) {
+ auto AddrOrErr = Sym.getAddress();
+ if (!AddrOrErr)
+ consumeError(AddrOrErr.takeError());
+ else
+ TrapOnFailFunctionAddresses.insert(*AddrOrErr);
+ }
+ }
+ if (auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Object)) {
+ for (const auto &Addr : ElfObject->getPltAddresses()) {
+ object::SymbolRef Sym(Addr.first, Object);
+ auto SymNameOrErr = Sym.getName();
+ if (!SymNameOrErr)
+ consumeError(SymNameOrErr.takeError());
+ else if (TrapOnFailFunctions.count(*SymNameOrErr) > 0)
+ TrapOnFailFunctionAddresses.insert(Addr.second);
+ }
+ }
+ return Error::success();
+}
+
UnsupportedDisassembly::UnsupportedDisassembly(StringRef Text) : Text(Text) {}
char UnsupportedDisassembly::ID;
diff --git a/tools/llvm-cfi-verify/lib/FileAnalysis.h b/tools/llvm-cfi-verify/lib/FileAnalysis.h
index 3f0a70487882..67421e80da37 100644
--- a/tools/llvm-cfi-verify/lib/FileAnalysis.h
+++ b/tools/llvm-cfi-verify/lib/FileAnalysis.h
@@ -11,6 +11,7 @@
#define LLVM_CFI_VERIFY_FILE_ANALYSIS_H
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallSet.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include "llvm/MC/MCAsmInfo.h"
@@ -110,6 +111,10 @@ public:
// Returns whether this instruction is used by CFI to trap the program.
bool isCFITrap(const Instr &InstrMeta) const;
+ // Returns whether this instruction is a call to a function that will trap on
+ // CFI violations (i.e., it serves as a trap in this instance).
+ bool willTrapOnCFIViolation(const Instr &InstrMeta) const;
+
// Returns whether this function can fall through to the next instruction.
// Undefined (and bad) instructions cannot fall through, and instruction that
// modify the control flow can only fall through if they are conditional
@@ -183,6 +188,10 @@ protected:
// internal members. Should only be called once by Create().
Error parseCodeSections();
+ // Parses the symbol table to look for the addresses of functions that will
+ // trap on CFI violations.
+ Error parseSymbolTable();
+
private:
// Members that describe the input file.
object::OwningBinary<object::Binary> Binary;
@@ -218,6 +227,9 @@ private:
// A list of addresses of indirect control flow instructions.
std::set<uint64_t> IndirectInstructions;
+
+ // The addresses of functions that will trap on CFI violations.
+ SmallSet<uint64_t, 4> TrapOnFailFunctionAddresses;
};
class UnsupportedDisassembly : public ErrorInfo<UnsupportedDisassembly> {
diff --git a/tools/llvm-cfi-verify/lib/GraphBuilder.cpp b/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
index 4153b5f6844a..5b2bc6f0c3bb 100644
--- a/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
+++ b/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
@@ -311,6 +311,24 @@ void GraphBuilder::buildFlowGraphImpl(const FileAnalysis &Analysis,
Result.ConditionalBranchNodes.push_back(BranchNode);
}
+ // When using cross-DSO, some indirect calls are not guarded by a branch to a
+ // trap but instead follow a call to __cfi_slowpath. For example:
+ // if (!InlinedFastCheck(f))
+ // call *f
+ // else {
+ // __cfi_slowpath(CallSiteTypeId, f);
+ // call *f
+ // }
+ // To mark the second call as protected, we recognize indirect calls that
+ // directly follow calls to functions that will trap on CFI violations.
+ if (CFCrossRefs.empty()) {
+ const Instr *PrevInstr = Analysis.getPrevInstructionSequential(ChildMeta);
+ if (PrevInstr && Analysis.willTrapOnCFIViolation(*PrevInstr)) {
+ Result.IntermediateNodes[PrevInstr->VMAddress] = Address;
+ HasValidCrossRef = true;
+ }
+ }
+
if (!HasValidCrossRef)
Result.OrphanedNodes.push_back(Address);
diff --git a/tools/llvm-config/CMakeLists.txt b/tools/llvm-config/CMakeLists.txt
index a0bd36c37315..a7db17386fb3 100644
--- a/tools/llvm-config/CMakeLists.txt
+++ b/tools/llvm-config/CMakeLists.txt
@@ -29,12 +29,20 @@ string(REPLACE ";" " " SYSTEM_LIBS "${SYSTEM_LIBS}")
# Fetch target specific compile options, e.g. RTTI option
get_property(COMPILE_FLAGS TARGET llvm-config PROPERTY COMPILE_FLAGS)
+# The language standard potentially affects the ABI/API of LLVM, so we want
+# to make sure it is reported by llvm-config.
+# NOTE: We don't want to start extracting any random C/CXX flags that the
+# user may add that could affect the ABI. We only want to extract flags
+# that have been added by the LLVM build system.
+string(REGEX MATCH "-std=[^ ]\+" LLVM_CXX_STD_FLAG ${CMAKE_CXX_FLAGS})
+string(REGEX MATCH "-std=[^ ]\+" LLVM_C_STD_FLAG ${CMAKE_C_FLAGS})
+
# Use configure_file to create BuildVariables.inc.
set(LLVM_SRC_ROOT ${LLVM_MAIN_SRC_DIR})
set(LLVM_OBJ_ROOT ${LLVM_BINARY_DIR})
-set(LLVM_CPPFLAGS "${CMAKE_CPP_FLAGS} ${CMAKE_CPP_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${LLVM_DEFINITIONS}")
-set(LLVM_CFLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${LLVM_DEFINITIONS}")
-set(LLVM_CXXFLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${COMPILE_FLAGS} ${LLVM_DEFINITIONS}")
+set(LLVM_CPPFLAGS "${LLVM_DEFINITIONS}")
+set(LLVM_CFLAGS "${LLVM_C_STD_FLAG} ${LLVM_DEFINITIONS}")
+set(LLVM_CXXFLAGS "${LLVM_CXX_STD_FLAG} ${COMPILE_FLAGS} ${LLVM_DEFINITIONS}")
set(LLVM_BUILD_SYSTEM cmake)
set(LLVM_HAS_RTTI ${LLVM_CONFIG_HAS_RTTI})
set(LLVM_DYLIB_VERSION "${LLVM_VERSION_MAJOR}${LLVM_VERSION_SUFFIX}")
diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp
index 892adc3b9dd8..bec89fef98ca 100644
--- a/tools/llvm-config/llvm-config.cpp
+++ b/tools/llvm-config/llvm-config.cpp
@@ -523,7 +523,7 @@ int main(int argc, char **argv) {
if (DyLibExists && !sys::fs::exists(path)) {
Components =
GetAllDyLibComponents(IsInDevelopmentTree, true, DirSep);
- llvm::sort(Components.begin(), Components.end());
+ llvm::sort(Components);
break;
}
}
diff --git a/tools/llvm-cov/CMakeLists.txt b/tools/llvm-cov/CMakeLists.txt
index d0416b06f9cd..c3afec86cba2 100644
--- a/tools/llvm-cov/CMakeLists.txt
+++ b/tools/llvm-cov/CMakeLists.txt
@@ -5,6 +5,7 @@ add_llvm_tool(llvm-cov
gcov.cpp
CodeCoverage.cpp
CoverageExporterJson.cpp
+ CoverageExporterLcov.cpp
CoverageFilters.cpp
CoverageReport.cpp
CoverageSummaryInfo.cpp
diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp
index e93b63d388e0..728e00e7c3c2 100644
--- a/tools/llvm-cov/CodeCoverage.cpp
+++ b/tools/llvm-cov/CodeCoverage.cpp
@@ -14,6 +14,7 @@
//===----------------------------------------------------------------------===//
#include "CoverageExporterJson.h"
+#include "CoverageExporterLcov.h"
#include "CoverageFilters.h"
#include "CoverageReport.h"
#include "CoverageSummaryInfo.h"
@@ -215,12 +216,13 @@ void CodeCoverageTool::collectPaths(const std::string &Path) {
for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E;
F != E; F.increment(EC)) {
- if (EC) {
- warning(EC.message(), F->path());
+ auto Status = F->status();
+ if (!Status) {
+ warning(Status.getError().message(), F->path());
continue;
}
- if (llvm::sys::fs::is_regular_file(F->path()))
+ if (Status->type() == llvm::sys::fs::file_type::regular_file)
addCollectedPath(F->path());
}
}
@@ -368,12 +370,6 @@ std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
<< "No profile record found for '" << HashMismatch.first << "'"
<< " with hash = 0x" << Twine::utohexstr(HashMismatch.second)
<< '\n';
-
- for (const auto &CounterMismatch : Coverage->getCounterMismatches())
- errs() << "counter-mismatch: "
- << "Coverage mapping for " << CounterMismatch.first
- << " only has " << CounterMismatch.second
- << " valid counter expressions\n";
}
}
@@ -571,7 +567,9 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
"Text output"),
clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html",
- "HTML output")),
+ "HTML output"),
+ clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov",
+ "lcov tracefile output")),
cl::init(CoverageViewOptions::OutputFormat::Text));
cl::opt<std::string> PathRemap(
@@ -679,6 +677,11 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
errs() << "Color output cannot be disabled when generating html.\n";
ViewOpts.Colors = true;
break;
+ case CoverageViewOptions::OutputFormat::Lcov:
+ if (UseColor == cl::BOU_TRUE)
+ errs() << "Color output cannot be enabled when generating lcov.\n";
+ ViewOpts.Colors = false;
+ break;
}
// If path-equivalence was given and is a comma seperated pair then set
@@ -688,7 +691,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
PathRemapping = EquivPair;
// If a demangler is supplied, check if it exists and register it.
- if (DemanglerOpts.size()) {
+ if (!DemanglerOpts.empty()) {
auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]);
if (!DemanglerPathOrErr) {
error("Could not find the demangler!",
@@ -838,6 +841,11 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
if (Err)
return Err;
+ if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
+ error("Lcov format should be used with 'llvm-cov export'.");
+ return 1;
+ }
+
ViewOpts.ShowLineNumbers = true;
ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
!ShowRegions || ShowBestLineRegionsCounts;
@@ -969,6 +977,9 @@ int CodeCoverageTool::doReport(int argc, const char **argv,
if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) {
error("HTML output for summary reports is not yet supported.");
return 1;
+ } else if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
+ error("Lcov format should be used with 'llvm-cov export'.");
+ return 1;
}
auto Coverage = load();
@@ -1000,8 +1011,10 @@ int CodeCoverageTool::doExport(int argc, const char **argv,
if (Err)
return Err;
- if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text) {
- error("Coverage data can only be exported as textual JSON.");
+ if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text &&
+ ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) {
+ error("Coverage data can only be exported as textual JSON or an "
+ "lcov tracefile.");
return 1;
}
@@ -1011,12 +1024,27 @@ int CodeCoverageTool::doExport(int argc, const char **argv,
return 1;
}
- auto Exporter = CoverageExporterJson(*Coverage.get(), ViewOpts, outs());
+ std::unique_ptr<CoverageExporter> Exporter;
+
+ switch (ViewOpts.Format) {
+ case CoverageViewOptions::OutputFormat::Text:
+ Exporter = llvm::make_unique<CoverageExporterJson>(*Coverage.get(),
+ ViewOpts, outs());
+ break;
+ case CoverageViewOptions::OutputFormat::HTML:
+ // Unreachable because we should have gracefully terminated with an error
+ // above.
+ llvm_unreachable("Export in HTML is not supported!");
+ case CoverageViewOptions::OutputFormat::Lcov:
+ Exporter = llvm::make_unique<CoverageExporterLcov>(*Coverage.get(),
+ ViewOpts, outs());
+ break;
+ }
if (SourceFiles.empty())
- Exporter.renderRoot(IgnoreFilenameFilters);
+ Exporter->renderRoot(IgnoreFilenameFilters);
else
- Exporter.renderRoot(SourceFiles);
+ Exporter->renderRoot(SourceFiles);
return 0;
}
diff --git a/tools/llvm-cov/CoverageExporter.h b/tools/llvm-cov/CoverageExporter.h
index 884fba96d618..b226d68813d9 100644
--- a/tools/llvm-cov/CoverageExporter.h
+++ b/tools/llvm-cov/CoverageExporter.h
@@ -30,7 +30,7 @@ protected:
/// The options passed to the tool.
const CoverageViewOptions &Options;
- /// Output stream to print JSON to.
+ /// Output stream to print to.
raw_ostream &OS;
CoverageExporter(const coverage::CoverageMapping &CoverageMapping,
@@ -41,10 +41,10 @@ public:
virtual ~CoverageExporter(){};
/// Render the CoverageMapping object.
- virtual void renderRoot(const CoverageFilters &IgnoreFilenameFilters) = 0;
+ virtual void renderRoot(const CoverageFilters &IgnoreFilters) = 0;
/// Render the CoverageMapping object for specified source files.
- virtual void renderRoot(const std::vector<std::string> &SourceFiles) = 0;
+ virtual void renderRoot(ArrayRef<std::string> SourceFiles) = 0;
};
} // end namespace llvm
diff --git a/tools/llvm-cov/CoverageExporterJson.cpp b/tools/llvm-cov/CoverageExporterJson.cpp
index 56c3a0003b02..22243f8e2c3e 100644
--- a/tools/llvm-cov/CoverageExporterJson.cpp
+++ b/tools/llvm-cov/CoverageExporterJson.cpp
@@ -16,33 +16,34 @@
// The json code coverage export follows the following format
// Root: dict => Root Element containing metadata
// -- Data: array => Homogeneous array of one or more export objects
-// ---- Export: dict => Json representation of one CoverageMapping
-// ------ Files: array => List of objects describing coverage for files
-// -------- File: dict => Coverage for a single file
-// ---------- Segments: array => List of Segments contained in the file
-// ------------ Segment: dict => Describes a segment of the file with a counter
-// ---------- Expansions: array => List of expansion records
-// ------------ Expansion: dict => Object that descibes a single expansion
-// -------------- CountedRegion: dict => The region to be expanded
-// -------------- TargetRegions: array => List of Regions in the expansion
-// ---------------- CountedRegion: dict => Single Region in the expansion
-// ---------- Summary: dict => Object summarizing the coverage for this file
-// ------------ LineCoverage: dict => Object summarizing line coverage
-// ------------ FunctionCoverage: dict => Object summarizing function coverage
-// ------------ RegionCoverage: dict => Object summarizing region coverage
-// ------ Functions: array => List of objects describing coverage for functions
-// -------- Function: dict => Coverage info for a single function
-// ---------- Filenames: array => List of filenames that the function relates to
-// ---- Summary: dict => Object summarizing the coverage for the entire binary
-// ------ LineCoverage: dict => Object summarizing line coverage
-// ------ FunctionCoverage: dict => Object summarizing function coverage
-// ------ InstantiationCoverage: dict => Object summarizing inst. coverage
-// ------ RegionCoverage: dict => Object summarizing region coverage
+// -- Export: dict => Json representation of one CoverageMapping
+// -- Files: array => List of objects describing coverage for files
+// -- File: dict => Coverage for a single file
+// -- Segments: array => List of Segments contained in the file
+// -- Segment: dict => Describes a segment of the file with a counter
+// -- Expansions: array => List of expansion records
+// -- Expansion: dict => Object that descibes a single expansion
+// -- CountedRegion: dict => The region to be expanded
+// -- TargetRegions: array => List of Regions in the expansion
+// -- CountedRegion: dict => Single Region in the expansion
+// -- Summary: dict => Object summarizing the coverage for this file
+// -- LineCoverage: dict => Object summarizing line coverage
+// -- FunctionCoverage: dict => Object summarizing function coverage
+// -- RegionCoverage: dict => Object summarizing region coverage
+// -- Functions: array => List of objects describing coverage for functions
+// -- Function: dict => Coverage info for a single function
+// -- Filenames: array => List of filenames that the function relates to
+// -- Summary: dict => Object summarizing the coverage for the entire binary
+// -- LineCoverage: dict => Object summarizing line coverage
+// -- FunctionCoverage: dict => Object summarizing function coverage
+// -- InstantiationCoverage: dict => Object summarizing inst. coverage
+// -- RegionCoverage: dict => Object summarizing region coverage
//
//===----------------------------------------------------------------------===//
#include "CoverageExporterJson.h"
#include "CoverageReport.h"
+#include "llvm/Support/JSON.h"
/// The semantic version combined as a string.
#define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.0"
@@ -52,335 +53,142 @@
using namespace llvm;
-CoverageExporterJson::CoverageExporterJson(
- const coverage::CoverageMapping &CoverageMapping,
- const CoverageViewOptions &Options, raw_ostream &OS)
- : CoverageExporter(CoverageMapping, Options, OS) {
- State.push(JsonState::None);
-}
-
-void CoverageExporterJson::emitSerialized(const int64_t Value) { OS << Value; }
+namespace {
-void CoverageExporterJson::emitSerialized(const std::string &Value) {
- OS << "\"";
- for (char C : Value) {
- if (C != '\\')
- OS << C;
- else
- OS << "\\\\";
- }
- OS << "\"";
+json::Array renderSegment(const coverage::CoverageSegment &Segment) {
+ return json::Array({Segment.Line, Segment.Col, int64_t(Segment.Count),
+ Segment.HasCount, Segment.IsRegionEntry});
}
-void CoverageExporterJson::emitComma() {
- if (State.top() == JsonState::NonEmptyElement) {
- OS << ",";
- } else if (State.top() == JsonState::EmptyElement) {
- State.pop();
- assert((State.size() >= 1) && "Closed too many JSON elements");
- State.push(JsonState::NonEmptyElement);
- }
+json::Array renderRegion(const coverage::CountedRegion &Region) {
+ return json::Array({Region.LineStart, Region.ColumnStart, Region.LineEnd,
+ Region.ColumnEnd, int64_t(Region.ExecutionCount),
+ Region.FileID, Region.ExpandedFileID,
+ int64_t(Region.Kind)});
}
-void CoverageExporterJson::emitDictStart() {
- emitComma();
- State.push(JsonState::EmptyElement);
- OS << "{";
+json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) {
+ json::Array RegionArray;
+ for (const auto &Region : Regions)
+ RegionArray.push_back(renderRegion(Region));
+ return RegionArray;
+}
+
+json::Object renderExpansion(const coverage::ExpansionRecord &Expansion) {
+ return json::Object(
+ {{"filenames", json::Array(Expansion.Function.Filenames)},
+ // Mark the beginning and end of this expansion in the source file.
+ {"source_region", renderRegion(Expansion.Region)},
+ // Enumerate the coverage information for the expansion.
+ {"target_regions", renderRegions(Expansion.Function.CountedRegions)}});
+}
+
+json::Object renderSummary(const FileCoverageSummary &Summary) {
+ return json::Object(
+ {{"lines",
+ json::Object({{"count", int64_t(Summary.LineCoverage.getNumLines())},
+ {"covered", int64_t(Summary.LineCoverage.getCovered())},
+ {"percent", Summary.LineCoverage.getPercentCovered()}})},
+ {"functions",
+ json::Object(
+ {{"count", int64_t(Summary.FunctionCoverage.getNumFunctions())},
+ {"covered", int64_t(Summary.FunctionCoverage.getExecuted())},
+ {"percent", Summary.FunctionCoverage.getPercentCovered()}})},
+ {"instantiations",
+ json::Object(
+ {{"count",
+ int64_t(Summary.InstantiationCoverage.getNumFunctions())},
+ {"covered", int64_t(Summary.InstantiationCoverage.getExecuted())},
+ {"percent", Summary.InstantiationCoverage.getPercentCovered()}})},
+ {"regions",
+ json::Object(
+ {{"count", int64_t(Summary.RegionCoverage.getNumRegions())},
+ {"covered", int64_t(Summary.RegionCoverage.getCovered())},
+ {"notcovered", int64_t(Summary.RegionCoverage.getNumRegions() -
+ Summary.RegionCoverage.getCovered())},
+ {"percent", Summary.RegionCoverage.getPercentCovered()}})}});
+}
+
+json::Array renderFileExpansions(const coverage::CoverageData &FileCoverage,
+ const FileCoverageSummary &FileReport) {
+ json::Array ExpansionArray;
+ for (const auto &Expansion : FileCoverage.getExpansions())
+ ExpansionArray.push_back(renderExpansion(Expansion));
+ return ExpansionArray;
}
-void CoverageExporterJson::emitDictKey(const std::string &Key) {
- emitComma();
- emitSerialized(Key);
- OS << ":";
- State.pop();
- assert((State.size() >= 1) && "Closed too many JSON elements");
-
- // We do not want to emit a comma after this key.
- State.push(JsonState::EmptyElement);
+json::Array renderFileSegments(const coverage::CoverageData &FileCoverage,
+ const FileCoverageSummary &FileReport) {
+ json::Array SegmentArray;
+ for (const auto &Segment : FileCoverage)
+ SegmentArray.push_back(renderSegment(Segment));
+ return SegmentArray;
}
-void CoverageExporterJson::emitDictEnd() {
- State.pop();
- assert((State.size() >= 1) && "Closed too many JSON elements");
- OS << "}";
+json::Object renderFile(const coverage::CoverageMapping &Coverage,
+ const std::string &Filename,
+ const FileCoverageSummary &FileReport,
+ bool ExportSummaryOnly) {
+ json::Object File({{"filename", Filename}});
+ if (!ExportSummaryOnly) {
+ // Calculate and render detailed coverage information for given file.
+ auto FileCoverage = Coverage.getCoverageForFile(Filename);
+ File["segments"] = renderFileSegments(FileCoverage, FileReport);
+ File["expansions"] = renderFileExpansions(FileCoverage, FileReport);
+ }
+ File["summary"] = renderSummary(FileReport);
+ return File;
}
-void CoverageExporterJson::emitArrayStart() {
- emitComma();
- State.push(JsonState::EmptyElement);
- OS << "[";
+json::Array renderFiles(const coverage::CoverageMapping &Coverage,
+ ArrayRef<std::string> SourceFiles,
+ ArrayRef<FileCoverageSummary> FileReports,
+ bool ExportSummaryOnly) {
+ json::Array FileArray;
+ for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I)
+ FileArray.push_back(renderFile(Coverage, SourceFiles[I], FileReports[I],
+ ExportSummaryOnly));
+ return FileArray;
}
-void CoverageExporterJson::emitArrayEnd() {
- State.pop();
- assert((State.size() >= 1) && "Closed too many JSON elements");
- OS << "]";
+json::Array renderFunctions(
+ const iterator_range<coverage::FunctionRecordIterator> &Functions) {
+ json::Array FunctionArray;
+ for (const auto &F : Functions)
+ FunctionArray.push_back(
+ json::Object({{"name", F.Name},
+ {"count", int64_t(F.ExecutionCount)},
+ {"regions", renderRegions(F.CountedRegions)},
+ {"filenames", json::Array(F.Filenames)}}));
+ return FunctionArray;
}
-void CoverageExporterJson::renderRoot(
- const CoverageFilters &IgnoreFilenameFilters) {
+} // end anonymous namespace
+
+void CoverageExporterJson::renderRoot(const CoverageFilters &IgnoreFilters) {
std::vector<std::string> SourceFiles;
for (StringRef SF : Coverage.getUniqueSourceFiles()) {
- if (!IgnoreFilenameFilters.matchesFilename(SF))
+ if (!IgnoreFilters.matchesFilename(SF))
SourceFiles.emplace_back(SF);
}
renderRoot(SourceFiles);
}
-void CoverageExporterJson::renderRoot(
- const std::vector<std::string> &SourceFiles) {
- // Start Root of JSON object.
- emitDictStart();
-
- emitDictElement("version", LLVM_COVERAGE_EXPORT_JSON_STR);
- emitDictElement("type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR);
- emitDictKey("data");
-
- // Start List of Exports.
- emitArrayStart();
-
- // Start Export.
- emitDictStart();
-
- emitDictKey("files");
-
+void CoverageExporterJson::renderRoot(ArrayRef<std::string> SourceFiles) {
FileCoverageSummary Totals = FileCoverageSummary("Totals");
auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
SourceFiles, Options);
- renderFiles(SourceFiles, FileReports);
-
+ auto Export =
+ json::Object({{"files", renderFiles(Coverage, SourceFiles, FileReports,
+ Options.ExportSummaryOnly)},
+ {"totals", renderSummary(Totals)}});
// Skip functions-level information for summary-only export mode.
- if (!Options.ExportSummaryOnly) {
- emitDictKey("functions");
- renderFunctions(Coverage.getCoveredFunctions());
- }
-
- emitDictKey("totals");
- renderSummary(Totals);
-
- // End Export.
- emitDictEnd();
-
- // End List of Exports.
- emitArrayEnd();
-
- // End Root of JSON Object.
- emitDictEnd();
-
- assert((State.top() == JsonState::None) &&
- "All Elements In JSON were Closed");
-}
-
-void CoverageExporterJson::renderFunctions(
- const iterator_range<coverage::FunctionRecordIterator> &Functions) {
- // Start List of Functions.
- emitArrayStart();
-
- for (const auto &Function : Functions) {
- // Start Function.
- emitDictStart();
-
- emitDictElement("name", Function.Name);
- emitDictElement("count", Function.ExecutionCount);
- emitDictKey("regions");
-
- renderRegions(Function.CountedRegions);
-
- emitDictKey("filenames");
-
- // Start Filenames for Function.
- emitArrayStart();
-
- for (const auto &FileName : Function.Filenames)
- emitArrayElement(FileName);
-
- // End Filenames for Function.
- emitArrayEnd();
-
- // End Function.
- emitDictEnd();
- }
-
- // End List of Functions.
- emitArrayEnd();
-}
-
-void CoverageExporterJson::renderFiles(
- ArrayRef<std::string> SourceFiles,
- ArrayRef<FileCoverageSummary> FileReports) {
- // Start List of Files.
- emitArrayStart();
-
- for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) {
- renderFile(SourceFiles[I], FileReports[I]);
- }
-
- // End List of Files.
- emitArrayEnd();
-}
-
-void CoverageExporterJson::renderFile(const std::string &Filename,
- const FileCoverageSummary &FileReport) {
- // Start File.
- emitDictStart();
-
- emitDictElement("filename", Filename);
-
- if (!Options.ExportSummaryOnly) {
- // Calculate and render detailed coverage information for given file.
- auto FileCoverage = Coverage.getCoverageForFile(Filename);
- renderFileCoverage(FileCoverage, FileReport);
- }
-
- emitDictKey("summary");
- renderSummary(FileReport);
-
- // End File.
- emitDictEnd();
-}
-
-
-void CoverageExporterJson::renderFileCoverage(
- const coverage::CoverageData &FileCoverage,
- const FileCoverageSummary &FileReport) {
- emitDictKey("segments");
-
- // Start List of Segments.
- emitArrayStart();
-
- for (const auto &Segment : FileCoverage)
- renderSegment(Segment);
-
- // End List of Segments.
- emitArrayEnd();
-
- emitDictKey("expansions");
-
- // Start List of Expansions.
- emitArrayStart();
-
- for (const auto &Expansion : FileCoverage.getExpansions())
- renderExpansion(Expansion);
-
- // End List of Expansions.
- emitArrayEnd();
-}
-
-void CoverageExporterJson::renderSegment(
- const coverage::CoverageSegment &Segment) {
- // Start Segment.
- emitArrayStart();
-
- emitArrayElement(Segment.Line);
- emitArrayElement(Segment.Col);
- emitArrayElement(Segment.Count);
- emitArrayElement(Segment.HasCount);
- emitArrayElement(Segment.IsRegionEntry);
-
- // End Segment.
- emitArrayEnd();
-}
-
-void CoverageExporterJson::renderExpansion(
- const coverage::ExpansionRecord &Expansion) {
- // Start Expansion.
- emitDictStart();
-
- // Mark the beginning and end of this expansion in the source file.
- emitDictKey("source_region");
- renderRegion(Expansion.Region);
-
- // Enumerate the coverage information for the expansion.
- emitDictKey("target_regions");
- renderRegions(Expansion.Function.CountedRegions);
-
- emitDictKey("filenames");
- // Start List of Filenames to map the fileIDs.
- emitArrayStart();
- for (const auto &Filename : Expansion.Function.Filenames)
- emitArrayElement(Filename);
- // End List of Filenames.
- emitArrayEnd();
-
- // End Expansion.
- emitDictEnd();
-}
-
-void CoverageExporterJson::renderRegions(
- ArrayRef<coverage::CountedRegion> Regions) {
- // Start List of Regions.
- emitArrayStart();
-
- for (const auto &Region : Regions)
- renderRegion(Region);
-
- // End List of Regions.
- emitArrayEnd();
-}
-
-void CoverageExporterJson::renderRegion(const coverage::CountedRegion &Region) {
- // Start CountedRegion.
- emitArrayStart();
-
- emitArrayElement(Region.LineStart);
- emitArrayElement(Region.ColumnStart);
- emitArrayElement(Region.LineEnd);
- emitArrayElement(Region.ColumnEnd);
- emitArrayElement(Region.ExecutionCount);
- emitArrayElement(Region.FileID);
- emitArrayElement(Region.ExpandedFileID);
- emitArrayElement(Region.Kind);
-
- // End CountedRegion.
- emitArrayEnd();
-}
-
-void CoverageExporterJson::renderSummary(const FileCoverageSummary &Summary) {
- // Start Summary for the file.
- emitDictStart();
-
- emitDictKey("lines");
-
- // Start Line Coverage Summary.
- emitDictStart();
- emitDictElement("count", Summary.LineCoverage.getNumLines());
- emitDictElement("covered", Summary.LineCoverage.getCovered());
- emitDictElement("percent", Summary.LineCoverage.getPercentCovered());
- // End Line Coverage Summary.
- emitDictEnd();
-
- emitDictKey("functions");
-
- // Start Function Coverage Summary.
- emitDictStart();
- emitDictElement("count", Summary.FunctionCoverage.getNumFunctions());
- emitDictElement("covered", Summary.FunctionCoverage.getExecuted());
- emitDictElement("percent", Summary.FunctionCoverage.getPercentCovered());
- // End Function Coverage Summary.
- emitDictEnd();
-
- emitDictKey("instantiations");
-
- // Start Instantiation Coverage Summary.
- emitDictStart();
- emitDictElement("count", Summary.InstantiationCoverage.getNumFunctions());
- emitDictElement("covered", Summary.InstantiationCoverage.getExecuted());
- emitDictElement("percent", Summary.InstantiationCoverage.getPercentCovered());
- // End Function Coverage Summary.
- emitDictEnd();
-
- emitDictKey("regions");
+ if (!Options.ExportSummaryOnly)
+ Export["functions"] = renderFunctions(Coverage.getCoveredFunctions());
- // Start Region Coverage Summary.
- emitDictStart();
- emitDictElement("count", Summary.RegionCoverage.getNumRegions());
- emitDictElement("covered", Summary.RegionCoverage.getCovered());
- emitDictElement("notcovered", Summary.RegionCoverage.getNumRegions() -
- Summary.RegionCoverage.getCovered());
- emitDictElement("percent", Summary.RegionCoverage.getPercentCovered());
- // End Region Coverage Summary.
- emitDictEnd();
+ auto ExportArray = json::Array({std::move(Export)});
- // End Summary for the file.
- emitDictEnd();
+ OS << json::Object({{"version", LLVM_COVERAGE_EXPORT_JSON_STR},
+ {"type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR},
+ {"data", std::move(ExportArray)}});
}
diff --git a/tools/llvm-cov/CoverageExporterJson.h b/tools/llvm-cov/CoverageExporterJson.h
index f88dffa0ebea..c37c86b42be9 100644
--- a/tools/llvm-cov/CoverageExporterJson.h
+++ b/tools/llvm-cov/CoverageExporterJson.h
@@ -15,96 +15,20 @@
#define LLVM_COV_COVERAGEEXPORTERJSON_H
#include "CoverageExporter.h"
-#include <stack>
namespace llvm {
class CoverageExporterJson : public CoverageExporter {
- /// States that the JSON rendering machine can be in.
- enum JsonState { None, NonEmptyElement, EmptyElement };
-
- /// Tracks state of the JSON output.
- std::stack<JsonState> State;
-
- /// Emit a serialized scalar.
- void emitSerialized(const int64_t Value);
-
- /// Emit a serialized string.
- void emitSerialized(const std::string &Value);
-
- /// Emit a comma if there is a previous element to delimit.
- void emitComma();
-
- /// Emit a starting dictionary/object character.
- void emitDictStart();
-
- /// Emit a dictionary/object key but no value.
- void emitDictKey(const std::string &Key);
-
- /// Emit a dictionary/object key/value pair.
- template <typename V>
- void emitDictElement(const std::string &Key, const V &Value) {
- emitComma();
- emitSerialized(Key);
- OS << ":";
- emitSerialized(Value);
- }
-
- /// Emit a closing dictionary/object character.
- void emitDictEnd();
-
- /// Emit a starting array character.
- void emitArrayStart();
-
- /// Emit an array element.
- template <typename V> void emitArrayElement(const V &Value) {
- emitComma();
- emitSerialized(Value);
- }
-
- /// emit a closing array character.
- void emitArrayEnd();
-
- /// Render an array of all the given functions.
- void renderFunctions(
- const iterator_range<coverage::FunctionRecordIterator> &Functions);
-
- /// Render an array of all the source files, also pass back a Summary.
- void renderFiles(ArrayRef<std::string> SourceFiles,
- ArrayRef<FileCoverageSummary> FileReports);
-
- /// Render a single file.
- void renderFile(const std::string &Filename,
- const FileCoverageSummary &FileReport);
-
- /// Render summary for a single file.
- void renderFileCoverage(const coverage::CoverageData &FileCoverage,
- const FileCoverageSummary &FileReport);
-
- /// Render a CoverageSegment.
- void renderSegment(const coverage::CoverageSegment &Segment);
-
- /// Render an ExpansionRecord.
- void renderExpansion(const coverage::ExpansionRecord &Expansion);
-
- /// Render a list of CountedRegions.
- void renderRegions(ArrayRef<coverage::CountedRegion> Regions);
-
- /// Render a single CountedRegion.
- void renderRegion(const coverage::CountedRegion &Region);
-
- /// Render a FileCoverageSummary.
- void renderSummary(const FileCoverageSummary &Summary);
-
public:
CoverageExporterJson(const coverage::CoverageMapping &CoverageMapping,
- const CoverageViewOptions &Options, raw_ostream &OS);
+ const CoverageViewOptions &Options, raw_ostream &OS)
+ : CoverageExporter(CoverageMapping, Options, OS) {}
/// Render the CoverageMapping object.
- void renderRoot(const CoverageFilters &IgnoreFilenameFilters) override;
+ void renderRoot(const CoverageFilters &IgnoreFilters) override;
/// Render the CoverageMapping object for specified source files.
- void renderRoot(const std::vector<std::string> &SourceFiles) override;
+ void renderRoot(ArrayRef<std::string> SourceFiles) override;
};
} // end namespace llvm
diff --git a/tools/llvm-cov/CoverageExporterLcov.cpp b/tools/llvm-cov/CoverageExporterLcov.cpp
new file mode 100644
index 000000000000..d149ba1a4c87
--- /dev/null
+++ b/tools/llvm-cov/CoverageExporterLcov.cpp
@@ -0,0 +1,125 @@
+//===- CoverageExporterLcov.cpp - Code coverage export --------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements export of code coverage data to lcov trace file format.
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+//
+// The trace file code coverage export follows the following format (see also
+// https://linux.die.net/man/1/geninfo). Each quoted string appears on its own
+// line; the indentation shown here is only for documentation purposes.
+//
+// - for each source file:
+// - "SF:<absolute path to source file>"
+// - for each function:
+// - "FN:<line number of function start>,<function name>"
+// - for each function:
+// - "FNDA:<execution count>,<function name>"
+// - "FNF:<number of functions found>"
+// - "FNH:<number of functions hit>"
+// - for each instrumented line:
+// - "DA:<line number>,<execution count>[,<checksum>]
+// - "LH:<number of lines with non-zero execution count>"
+// - "LF:<nubmer of instrumented lines>"
+// - "end_of_record"
+//
+// If the user is exporting summary information only, then the FN, FNDA, and DA
+// lines will not be present.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoverageExporterLcov.h"
+#include "CoverageReport.h"
+
+using namespace llvm;
+
+namespace {
+
+void renderFunctionSummary(raw_ostream &OS,
+ const FileCoverageSummary &Summary) {
+ OS << "FNF:" << Summary.FunctionCoverage.getNumFunctions() << '\n'
+ << "FNH:" << Summary.FunctionCoverage.getExecuted() << '\n';
+}
+
+void renderFunctions(
+ raw_ostream &OS,
+ const iterator_range<coverage::FunctionRecordIterator> &Functions) {
+ for (const auto &F : Functions) {
+ auto StartLine = F.CountedRegions.front().LineStart;
+ OS << "FN:" << StartLine << ',' << F.Name << '\n';
+ }
+ for (const auto &F : Functions)
+ OS << "FNDA:" << F.ExecutionCount << ',' << F.Name << '\n';
+}
+
+void renderLineExecutionCounts(raw_ostream &OS,
+ const coverage::CoverageData &FileCoverage) {
+ coverage::LineCoverageIterator LCI{FileCoverage, 1};
+ coverage::LineCoverageIterator LCIEnd = LCI.getEnd();
+ for (; LCI != LCIEnd; ++LCI) {
+ const coverage::LineCoverageStats &LCS = *LCI;
+ if (LCS.isMapped()) {
+ OS << "DA:" << LCS.getLine() << ',' << LCS.getExecutionCount() << '\n';
+ }
+ }
+}
+
+void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
+ OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n'
+ << "LH:" << Summary.LineCoverage.getCovered() << '\n';
+}
+
+void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
+ const std::string &Filename,
+ const FileCoverageSummary &FileReport, bool ExportSummaryOnly) {
+ OS << "SF:" << Filename << '\n';
+
+ if (!ExportSummaryOnly) {
+ renderFunctions(OS, Coverage.getCoveredFunctions());
+ }
+ renderFunctionSummary(OS, FileReport);
+
+ if (!ExportSummaryOnly) {
+ // Calculate and render detailed coverage information for given file.
+ auto FileCoverage = Coverage.getCoverageForFile(Filename);
+ renderLineExecutionCounts(OS, FileCoverage);
+ }
+ renderLineSummary(OS, FileReport);
+
+ OS << "end_of_record\n";
+}
+
+void renderFiles(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
+ ArrayRef<std::string> SourceFiles,
+ ArrayRef<FileCoverageSummary> FileReports,
+ bool ExportSummaryOnly) {
+ for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I)
+ renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly);
+}
+
+} // end anonymous namespace
+
+void CoverageExporterLcov::renderRoot(const CoverageFilters &IgnoreFilters) {
+ std::vector<std::string> SourceFiles;
+ for (StringRef SF : Coverage.getUniqueSourceFiles()) {
+ if (!IgnoreFilters.matchesFilename(SF))
+ SourceFiles.emplace_back(SF);
+ }
+ renderRoot(SourceFiles);
+}
+
+void CoverageExporterLcov::renderRoot(ArrayRef<std::string> SourceFiles) {
+ FileCoverageSummary Totals = FileCoverageSummary("Totals");
+ auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
+ SourceFiles, Options);
+ renderFiles(OS, Coverage, SourceFiles, FileReports,
+ Options.ExportSummaryOnly);
+}
diff --git a/tools/llvm-cov/CoverageExporterLcov.h b/tools/llvm-cov/CoverageExporterLcov.h
new file mode 100644
index 000000000000..539b2dacd384
--- /dev/null
+++ b/tools/llvm-cov/CoverageExporterLcov.h
@@ -0,0 +1,36 @@
+//===- CoverageExporterLcov.h - Code coverage lcov exporter ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements a code coverage exporter for lcov trace file format.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGEEXPORTERLCOV_H
+#define LLVM_COV_COVERAGEEXPORTERLCOV_H
+
+#include "CoverageExporter.h"
+
+namespace llvm {
+
+class CoverageExporterLcov : public CoverageExporter {
+public:
+ CoverageExporterLcov(const coverage::CoverageMapping &CoverageMapping,
+ const CoverageViewOptions &Options, raw_ostream &OS)
+ : CoverageExporter(CoverageMapping, Options, OS) {}
+
+ /// Render the CoverageMapping object.
+ void renderRoot(const CoverageFilters &IgnoreFilters) override;
+
+ /// Render the CoverageMapping object for specified source files.
+ void renderRoot(ArrayRef<std::string> SourceFiles) override;
+};
+
+} // end namespace llvm
+
+#endif // LLVM_COV_COVERAGEEXPORTERLCOV_H
diff --git a/tools/llvm-cov/CoverageViewOptions.h b/tools/llvm-cov/CoverageViewOptions.h
index 20085a957bb5..c8a472860027 100644
--- a/tools/llvm-cov/CoverageViewOptions.h
+++ b/tools/llvm-cov/CoverageViewOptions.h
@@ -20,7 +20,8 @@ namespace llvm {
struct CoverageViewOptions {
enum class OutputFormat {
Text,
- HTML
+ HTML,
+ Lcov
};
bool Debug;
diff --git a/tools/llvm-cov/SourceCoverageView.cpp b/tools/llvm-cov/SourceCoverageView.cpp
index a5a8fa9a4814..cebaf63adb12 100644
--- a/tools/llvm-cov/SourceCoverageView.cpp
+++ b/tools/llvm-cov/SourceCoverageView.cpp
@@ -31,7 +31,7 @@ void CoveragePrinter::StreamDestructor::operator()(raw_ostream *OS) const {
std::string CoveragePrinter::getOutputPath(StringRef Path, StringRef Extension,
bool InToplevel,
bool Relative) const {
- assert(Extension.size() && "The file extension may not be empty");
+ assert(!Extension.empty() && "The file extension may not be empty");
SmallString<256> FullPath;
@@ -80,6 +80,10 @@ CoveragePrinter::create(const CoverageViewOptions &Opts) {
return llvm::make_unique<CoveragePrinterText>(Opts);
case CoverageViewOptions::OutputFormat::HTML:
return llvm::make_unique<CoveragePrinterHTML>(Opts);
+ case CoverageViewOptions::OutputFormat::Lcov:
+ // Unreachable because CodeCoverage.cpp should terminate with an error
+ // before we get here.
+ llvm_unreachable("Lcov format is not supported!");
}
llvm_unreachable("Unknown coverage output format!");
}
@@ -143,6 +147,10 @@ SourceCoverageView::create(StringRef SourceName, const MemoryBuffer &File,
case CoverageViewOptions::OutputFormat::HTML:
return llvm::make_unique<SourceCoverageViewHTML>(
SourceName, File, Options, std::move(CoverageInfo));
+ case CoverageViewOptions::OutputFormat::Lcov:
+ // Unreachable because CodeCoverage.cpp should terminate with an error
+ // before we get here.
+ llvm_unreachable("Lcov format is not supported!");
}
llvm_unreachable("Unknown coverage output format!");
}
diff --git a/tools/llvm-cov/SourceCoverageViewHTML.cpp b/tools/llvm-cov/SourceCoverageViewHTML.cpp
index acb67aa5cfc7..3f730bb7bc82 100644
--- a/tools/llvm-cov/SourceCoverageViewHTML.cpp
+++ b/tools/llvm-cov/SourceCoverageViewHTML.cpp
@@ -54,7 +54,7 @@ std::string escape(StringRef Str, const CoverageViewOptions &Opts) {
std::string tag(const std::string &Name, const std::string &Str,
const std::string &ClassName = "") {
std::string Tag = "<" + Name;
- if (ClassName != "")
+ if (!ClassName.empty())
Tag += " class='" + ClassName + "'";
return Tag + ">" + Str + "</" + Name + ">";
}
diff --git a/tools/llvm-cov/TestingSupport.cpp b/tools/llvm-cov/TestingSupport.cpp
index e07abdbd17f1..16a1c2665299 100644
--- a/tools/llvm-cov/TestingSupport.cpp
+++ b/tools/llvm-cov/TestingSupport.cpp
@@ -33,7 +33,7 @@ int convertForTestingMain(int argc, const char *argv[]) {
if (!ObjErr) {
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(ObjErr.takeError(), OS, "");
+ logAllUnhandledErrors(ObjErr.takeError(), OS);
OS.flush();
errs() << "error: " << Buf;
return 1;
diff --git a/tools/llvm-cvtres/llvm-cvtres.cpp b/tools/llvm-cvtres/llvm-cvtres.cpp
index 5d8e6ab3929b..53989c8191fa 100644
--- a/tools/llvm-cvtres/llvm-cvtres.cpp
+++ b/tools/llvm-cvtres/llvm-cvtres.cpp
@@ -103,7 +103,7 @@ int main(int Argc, const char **Argv) {
opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
if (InputArgs.hasArg(OPT_HELP)) {
- T.PrintHelp(outs(), "cvtres", "Resource Converter", false);
+ T.PrintHelp(outs(), "llvm-cvtres [options] file...", "Resource Converter", false);
return 0;
}
diff --git a/tools/llvm-cxxdump/llvm-cxxdump.cpp b/tools/llvm-cxxdump/llvm-cxxdump.cpp
index 09e40d9b0db7..7594066a395d 100644
--- a/tools/llvm-cxxdump/llvm-cxxdump.cpp
+++ b/tools/llvm-cxxdump/llvm-cxxdump.cpp
@@ -23,6 +23,7 @@
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
#include <string>
@@ -43,17 +44,18 @@ namespace llvm {
static void error(std::error_code EC) {
if (!EC)
return;
- outs() << "\nError reading file: " << EC.message() << ".\n";
+ WithColor::error(outs(), "") << "reading file: " << EC.message() << ".\n";
outs().flush();
exit(1);
}
static void error(Error Err) {
- if (Err) {
- logAllUnhandledErrors(std::move(Err), outs(), "Error reading file: ");
- outs().flush();
- exit(1);
- }
+ if (!Err)
+ return;
+ logAllUnhandledErrors(std::move(Err), WithColor::error(outs()),
+ "reading file: ");
+ outs().flush();
+ exit(1);
}
} // namespace llvm
@@ -61,7 +63,7 @@ static void error(Error Err) {
static void reportError(StringRef Input, StringRef Message) {
if (Input == "-")
Input = "<stdin>";
- errs() << Input << ": " << Message << "\n";
+ WithColor::error(errs(), Input) << Message << "\n";
errs().flush();
exit(1);
}
@@ -496,7 +498,7 @@ static void dumpArchive(const Archive *Arc) {
if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) {
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(std::move(E), OS, "");
+ logAllUnhandledErrors(std::move(E), OS);
OS.flush();
reportError(Arc->getFileName(), Buf);
}
diff --git a/tools/llvm-cxxmap/CMakeLists.txt b/tools/llvm-cxxmap/CMakeLists.txt
new file mode 100644
index 000000000000..e180d3cd82ff
--- /dev/null
+++ b/tools/llvm-cxxmap/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(LLVM_LINK_COMPONENTS
+ Core
+ Support
+ )
+
+add_llvm_tool(llvm-cxxmap
+ llvm-cxxmap.cpp
+ )
diff --git a/tools/llvm-cxxmap/LLVMBuild.txt b/tools/llvm-cxxmap/LLVMBuild.txt
new file mode 100644
index 000000000000..2876c4a2d1f2
--- /dev/null
+++ b/tools/llvm-cxxmap/LLVMBuild.txt
@@ -0,0 +1,22 @@
+;===- ./tools/llvm-cxxmap/LLVMBuild.txt ------------------------*- Conf -*--===;
+;
+; The LLVM Compiler Infrastructure
+;
+; This file is distributed under the University of Illinois Open Source
+; License. See LICENSE.TXT for details.
+;
+;===------------------------------------------------------------------------===;
+;
+; This is an LLVMBuild description file for the components in this subdirectory.
+;
+; For more information on the LLVMBuild system, please see:
+;
+; http://llvm.org/docs/LLVMBuild.html
+;
+;===------------------------------------------------------------------------===;
+
+[component_0]
+type = Tool
+name = llvm-cxxmap
+parent = Tools
+required_libraries = Support
diff --git a/tools/llvm-cxxmap/llvm-cxxmap.cpp b/tools/llvm-cxxmap/llvm-cxxmap.cpp
new file mode 100644
index 000000000000..39028cc86723
--- /dev/null
+++ b/tools/llvm-cxxmap/llvm-cxxmap.cpp
@@ -0,0 +1,155 @@
+//===- llvm-cxxmap.cpp ----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// llvm-cxxmap computes a correspondence between old symbol names and new
+// symbol names based on a symbol equivalence file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SymbolRemappingReader.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+cl::opt<std::string> OldSymbolFile(cl::Positional, cl::Required,
+ cl::desc("<symbol-file>"));
+cl::opt<std::string> NewSymbolFile(cl::Positional, cl::Required,
+ cl::desc("<symbol-file>"));
+cl::opt<std::string> RemappingFile("remapping-file", cl::Required,
+ cl::desc("Remapping file"));
+cl::alias RemappingFileA("r", cl::aliasopt(RemappingFile));
+cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
+ cl::init("-"), cl::desc("Output file"));
+cl::alias OutputFilenameA("o", cl::aliasopt(OutputFilename));
+
+cl::opt<bool> WarnAmbiguous(
+ "Wambiguous",
+ cl::desc("Warn on equivalent symbols in the output symbol list"));
+cl::opt<bool> WarnIncomplete(
+ "Wincomplete",
+ cl::desc("Warn on input symbols missing from output symbol list"));
+
+static void warn(Twine Message, Twine Whence = "",
+ std::string Hint = "") {
+ WithColor::warning();
+ std::string WhenceStr = Whence.str();
+ if (!WhenceStr.empty())
+ errs() << WhenceStr << ": ";
+ errs() << Message << "\n";
+ if (!Hint.empty())
+ WithColor::note() << Hint << "\n";
+}
+
+static void exitWithError(Twine Message, Twine Whence = "",
+ std::string Hint = "") {
+ WithColor::error();
+ std::string WhenceStr = Whence.str();
+ if (!WhenceStr.empty())
+ errs() << WhenceStr << ": ";
+ errs() << Message << "\n";
+ if (!Hint.empty())
+ WithColor::note() << Hint << "\n";
+ ::exit(1);
+}
+
+static void exitWithError(Error E, StringRef Whence = "") {
+ exitWithError(toString(std::move(E)), Whence);
+}
+
+static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
+ exitWithError(EC.message(), Whence);
+}
+
+static void remapSymbols(MemoryBuffer &OldSymbolFile,
+ MemoryBuffer &NewSymbolFile,
+ MemoryBuffer &RemappingFile,
+ raw_ostream &Out) {
+ // Load the remapping file and prepare to canonicalize symbols.
+ SymbolRemappingReader Reader;
+ if (Error E = Reader.read(RemappingFile))
+ exitWithError(std::move(E));
+
+ // Canonicalize the new symbols.
+ DenseMap<SymbolRemappingReader::Key, StringRef> MappedNames;
+ DenseSet<StringRef> UnparseableSymbols;
+ for (line_iterator LineIt(NewSymbolFile, /*SkipBlanks=*/true, '#');
+ !LineIt.is_at_eof(); ++LineIt) {
+ StringRef Symbol = *LineIt;
+
+ auto K = Reader.insert(Symbol);
+ if (!K) {
+ UnparseableSymbols.insert(Symbol);
+ continue;
+ }
+
+ auto ItAndIsNew = MappedNames.insert({K, Symbol});
+ if (WarnAmbiguous && !ItAndIsNew.second &&
+ ItAndIsNew.first->second != Symbol) {
+ warn("symbol " + Symbol + " is equivalent to earlier symbol " +
+ ItAndIsNew.first->second,
+ NewSymbolFile.getBufferIdentifier() + ":" +
+ Twine(LineIt.line_number()),
+ "later symbol will not be the target of any remappings");
+ }
+ }
+
+ // Figure out which new symbol each old symbol is equivalent to.
+ for (line_iterator LineIt(OldSymbolFile, /*SkipBlanks=*/true, '#');
+ !LineIt.is_at_eof(); ++LineIt) {
+ StringRef Symbol = *LineIt;
+
+ auto K = Reader.lookup(Symbol);
+ StringRef NewSymbol = MappedNames.lookup(K);
+
+ if (NewSymbol.empty()) {
+ if (WarnIncomplete && !UnparseableSymbols.count(Symbol)) {
+ warn("no new symbol matches old symbol " + Symbol,
+ OldSymbolFile.getBufferIdentifier() + ":" +
+ Twine(LineIt.line_number()));
+ }
+ continue;
+ }
+
+ Out << Symbol << " " << NewSymbol << "\n";
+ }
+}
+
+int main(int argc, const char *argv[]) {
+ InitLLVM X(argc, argv);
+
+ cl::ParseCommandLineOptions(argc, argv, "LLVM C++ mangled name remapper\n");
+
+ auto OldSymbolBufOrError = MemoryBuffer::getFileOrSTDIN(OldSymbolFile);
+ if (!OldSymbolBufOrError)
+ exitWithErrorCode(OldSymbolBufOrError.getError(), OldSymbolFile);
+
+ auto NewSymbolBufOrError = MemoryBuffer::getFileOrSTDIN(NewSymbolFile);
+ if (!NewSymbolBufOrError)
+ exitWithErrorCode(NewSymbolBufOrError.getError(), NewSymbolFile);
+
+ auto RemappingBufOrError = MemoryBuffer::getFileOrSTDIN(RemappingFile);
+ if (!RemappingBufOrError)
+ exitWithErrorCode(RemappingBufOrError.getError(), RemappingFile);
+
+ std::error_code EC;
+ raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text);
+ if (EC)
+ exitWithErrorCode(EC, OutputFilename);
+
+ remapSymbols(*OldSymbolBufOrError.get(), *NewSymbolBufOrError.get(),
+ *RemappingBufOrError.get(), OS);
+}
diff --git a/tools/llvm-diff/DifferenceEngine.cpp b/tools/llvm-diff/DifferenceEngine.cpp
index af0a055ea21f..acff8bb3e89b 100644
--- a/tools/llvm-diff/DifferenceEngine.cpp
+++ b/tools/llvm-diff/DifferenceEngine.cpp
@@ -629,8 +629,8 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart,
// If the terminators have different kinds, but one is an invoke and the
// other is an unconditional branch immediately following a call, unify
// the results and the destinations.
- TerminatorInst *LTerm = LStart->getParent()->getTerminator();
- TerminatorInst *RTerm = RStart->getParent()->getTerminator();
+ Instruction *LTerm = LStart->getParent()->getTerminator();
+ Instruction *RTerm = RStart->getParent()->getTerminator();
if (isa<BranchInst>(LTerm) && isa<InvokeInst>(RTerm)) {
if (cast<BranchInst>(LTerm)->isConditional()) return;
BasicBlock::iterator I = LTerm->getIterator();
@@ -686,9 +686,18 @@ void DifferenceEngine::diff(Module *L, Module *R) {
StringSet<> LNames;
SmallVector<std::pair<Function*,Function*>, 20> Queue;
+ unsigned LeftAnonCount = 0;
+ unsigned RightAnonCount = 0;
+
for (Module::iterator I = L->begin(), E = L->end(); I != E; ++I) {
Function *LFn = &*I;
- LNames.insert(LFn->getName());
+ StringRef Name = LFn->getName();
+ if (Name.empty()) {
+ ++LeftAnonCount;
+ continue;
+ }
+
+ LNames.insert(Name);
if (Function *RFn = R->getFunction(LFn->getName()))
Queue.push_back(std::make_pair(LFn, RFn));
@@ -698,10 +707,25 @@ void DifferenceEngine::diff(Module *L, Module *R) {
for (Module::iterator I = R->begin(), E = R->end(); I != E; ++I) {
Function *RFn = &*I;
- if (!LNames.count(RFn->getName()))
+ StringRef Name = RFn->getName();
+ if (Name.empty()) {
+ ++RightAnonCount;
+ continue;
+ }
+
+ if (!LNames.count(Name))
logf("function %r exists only in right module") << RFn;
}
+
+ if (LeftAnonCount != 0 || RightAnonCount != 0) {
+ SmallString<32> Tmp;
+ logf(("not comparing " + Twine(LeftAnonCount) +
+ " anonymous functions in the left module and " +
+ Twine(RightAnonCount) + " in the right module")
+ .toStringRef(Tmp));
+ }
+
for (SmallVectorImpl<std::pair<Function*,Function*> >::iterator
I = Queue.begin(), E = Queue.end(); I != E; ++I)
diff(I->first, I->second);
diff --git a/tools/llvm-dwarfdump/Statistics.cpp b/tools/llvm-dwarfdump/Statistics.cpp
index 5af853d4ef28..5fe7e8b4615b 100644
--- a/tools/llvm-dwarfdump/Statistics.cpp
+++ b/tools/llvm-dwarfdump/Statistics.cpp
@@ -1,4 +1,6 @@
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
@@ -18,18 +20,27 @@ struct PerFunctionStats {
/// Number of constants with location across all inlined instances.
unsigned ConstantMembers = 0;
/// List of all Variables in this function.
- SmallDenseSet<uint32_t, 4> VarsInFunction;
+ StringSet<> VarsInFunction;
/// Compile units also cover a PC range, but have this flag set to false.
bool IsFunction = false;
};
-/// Holds accumulated global statistics about local variables.
+/// Holds accumulated global statistics about DIEs.
struct GlobalStats {
/// Total number of PC range bytes covered by DW_AT_locations.
unsigned ScopeBytesCovered = 0;
/// Total number of PC range bytes in each variable's enclosing scope,
/// starting from the first definition of the variable.
unsigned ScopeBytesFromFirstDefinition = 0;
+ /// Total number of call site entries (DW_TAG_call_site).
+ unsigned CallSiteEntries = 0;
+ /// Total byte size of concrete functions. This byte size includes
+ /// inline functions contained in the concrete functions.
+ uint64_t FunctionSize = 0;
+ /// Total byte size of inlined functions. This is the total number of bytes
+ /// for the top inline functions within concrete functions. This can help
+ /// tune the inline settings when compiling to match user expectations.
+ uint64_t InlineFunctionSize = 0;
};
/// Extract the low pc from a Die.
@@ -46,19 +57,37 @@ static uint64_t getLowPC(DWARFDie Die) {
}
/// Collect debug info quality metrics for one DIE.
-static void collectStatsForDie(DWARFDie Die, std::string Prefix,
- uint64_t ScopeLowPC, uint64_t BytesInScope,
+static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
+ std::string VarPrefix, uint64_t ScopeLowPC,
+ uint64_t BytesInScope,
+ uint32_t InlineDepth,
StringMap<PerFunctionStats> &FnStatMap,
GlobalStats &GlobalStats) {
bool HasLoc = false;
uint64_t BytesCovered = 0;
uint64_t OffsetToFirstDefinition = 0;
+
+ if (Die.getTag() == dwarf::DW_TAG_call_site) {
+ GlobalStats.CallSiteEntries++;
+ return;
+ }
+
+ if (Die.getTag() != dwarf::DW_TAG_formal_parameter &&
+ Die.getTag() != dwarf::DW_TAG_variable &&
+ Die.getTag() != dwarf::DW_TAG_member) {
+ // Not a variable or constant member.
+ return;
+ }
+
if (Die.find(dwarf::DW_AT_const_value)) {
// This catches constant members *and* variables.
HasLoc = true;
BytesCovered = BytesInScope;
- } else if (Die.getTag() == dwarf::DW_TAG_variable ||
- Die.getTag() == dwarf::DW_TAG_formal_parameter) {
+ } else {
+ if (Die.getTag() == dwarf::DW_TAG_member) {
+ // Non-const member.
+ return;
+ }
// Handle variables and function arguments.
auto FormValue = Die.find(dwarf::DW_AT_location);
HasLoc = FormValue.hasValue();
@@ -86,19 +115,17 @@ static void collectStatsForDie(DWARFDie Die, std::string Prefix,
BytesCovered = BytesInScope;
}
}
- } else {
- // Not a variable or constant member.
- return;
}
// Collect PC range coverage data.
- auto &FnStats = FnStatMap[Prefix];
+ auto &FnStats = FnStatMap[FnPrefix];
if (DWARFDie D =
Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
Die = D;
- // This is a unique ID for the variable inside the current object file.
- unsigned CanonicalDieOffset = Die.getOffset();
- FnStats.VarsInFunction.insert(CanonicalDieOffset);
+ // By using the variable name + the path through the lexical block tree, the
+ // keys are consistent across duplicate abstract origins in different CUs.
+ std::string VarName = StringRef(Die.getName(DINameKind::ShortName));
+ FnStats.VarsInFunction.insert(VarPrefix+VarName);
if (BytesInScope) {
FnStats.TotalVarWithLoc += (unsigned)HasLoc;
// Adjust for the fact the variables often start their lifetime in the
@@ -115,24 +142,34 @@ static void collectStatsForDie(DWARFDie Die, std::string Prefix,
}
/// Recursively collect debug info quality metrics.
-static void collectStatsRecursive(DWARFDie Die, std::string Prefix,
- uint64_t ScopeLowPC, uint64_t BytesInScope,
+static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
+ std::string VarPrefix, uint64_t ScopeLowPC,
+ uint64_t BytesInScope,
+ uint32_t InlineDepth,
StringMap<PerFunctionStats> &FnStatMap,
GlobalStats &GlobalStats) {
// Handle any kind of lexical scope.
- if (Die.getTag() == dwarf::DW_TAG_subprogram ||
- Die.getTag() == dwarf::DW_TAG_inlined_subroutine ||
- Die.getTag() == dwarf::DW_TAG_lexical_block) {
+ const dwarf::Tag Tag = Die.getTag();
+ const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
+ const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
+ const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
+ if (IsFunction || IsInlinedFunction || IsBlock) {
+
+ // Reset VarPrefix when entering a new function.
+ if (Die.getTag() == dwarf::DW_TAG_subprogram ||
+ Die.getTag() == dwarf::DW_TAG_inlined_subroutine)
+ VarPrefix = "v";
+
// Ignore forward declarations.
if (Die.find(dwarf::DW_AT_declaration))
return;
// Count the function.
- if (Die.getTag() != dwarf::DW_TAG_lexical_block) {
+ if (!IsBlock) {
StringRef Name = Die.getName(DINameKind::LinkageName);
if (Name.empty())
Name = Die.getName(DINameKind::ShortName);
- Prefix = Name;
+ FnPrefix = Name;
// Skip over abstract origins.
if (Die.find(dwarf::DW_AT_inline))
return;
@@ -148,26 +185,42 @@ static void collectStatsRecursive(DWARFDie Die, std::string Prefix,
llvm::consumeError(RangesOrError.takeError());
return;
}
-
+
auto Ranges = RangesOrError.get();
uint64_t BytesInThisScope = 0;
for (auto Range : Ranges)
BytesInThisScope += Range.HighPC - Range.LowPC;
ScopeLowPC = getLowPC(Die);
- if (BytesInThisScope)
+ if (BytesInThisScope) {
BytesInScope = BytesInThisScope;
+ if (IsFunction)
+ GlobalStats.FunctionSize += BytesInThisScope;
+ else if (IsInlinedFunction && InlineDepth == 0)
+ GlobalStats.InlineFunctionSize += BytesInThisScope;
+ }
} else {
// Not a scope, visit the Die itself. It could be a variable.
- collectStatsForDie(Die, Prefix, ScopeLowPC, BytesInScope, FnStatMap,
- GlobalStats);
+ collectStatsForDie(Die, FnPrefix, VarPrefix, ScopeLowPC, BytesInScope,
+ InlineDepth, FnStatMap, GlobalStats);
}
+ // Set InlineDepth correctly for child recursion
+ if (IsFunction)
+ InlineDepth = 0;
+ else if (IsInlinedFunction)
+ ++InlineDepth;
+
// Traverse children.
+ unsigned LexicalBlockIndex = 0;
DWARFDie Child = Die.getFirstChild();
while (Child) {
- collectStatsRecursive(Child, Prefix, ScopeLowPC, BytesInScope, FnStatMap,
- GlobalStats);
+ std::string ChildVarPrefix = VarPrefix;
+ if (Child.getTag() == dwarf::DW_TAG_lexical_block)
+ ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
+
+ collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, ScopeLowPC,
+ BytesInScope, InlineDepth, FnStatMap, GlobalStats);
Child = Child.getSibling();
}
}
@@ -200,7 +253,7 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
StringMap<PerFunctionStats> Statistics;
for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units())
if (DWARFDie CUDie = CU->getUnitDIE(false))
- collectStatsRecursive(CUDie, "/", 0, 0, Statistics, GlobalStats);
+ collectStatsRecursive(CUDie, "/", "g", 0, 0, 0, Statistics, GlobalStats);
/// The version number should be increased every time the algorithm is changed
/// (including bug fixes). New metrics may be added without increasing the
@@ -218,16 +271,15 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
VarWithLoc += Stats.TotalVarWithLoc + Constants;
VarTotal += TotalVars + Constants;
VarUnique += Stats.VarsInFunction.size();
- LLVM_DEBUG(for (auto V
- : Stats.VarsInFunction) llvm::dbgs()
- << Entry.getKey() << ": " << V << "\n");
+ LLVM_DEBUG(for (auto &V : Stats.VarsInFunction) llvm::dbgs()
+ << Entry.getKey() << ": " << V.getKey() << "\n");
NumFunctions += Stats.IsFunction;
NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
}
// Print summary.
OS.SetBufferSize(1024);
- OS << "{\"version\":\"" << Version << '"';
+ OS << "{\"version\":" << Version;
LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
llvm::dbgs() << "---------------------------------\n");
printDatum(OS, "file", Filename.str());
@@ -237,9 +289,12 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
printDatum(OS, "unique source variables", VarUnique);
printDatum(OS, "source variables", VarTotal);
printDatum(OS, "variables with location", VarWithLoc);
+ printDatum(OS, "call site entries", GlobalStats.CallSiteEntries);
printDatum(OS, "scope bytes total",
GlobalStats.ScopeBytesFromFirstDefinition);
printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered);
+ printDatum(OS, "total function size", GlobalStats.FunctionSize);
+ printDatum(OS, "total inlined function size", GlobalStats.InlineFunctionSize);
OS << "}\n";
LLVM_DEBUG(
llvm::dbgs() << "Total Availability: "
diff --git a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
index d75f33906098..d9e8e36efe5c 100644
--- a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
+++ b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
@@ -156,7 +156,7 @@ static list<std::string> Name(
value_desc("pattern"), cat(DwarfDumpCategory));
static alias NameAlias("n", desc("Alias for -name"), aliasopt(Name));
static opt<unsigned long long> Lookup("lookup",
- desc("Lookup <address> in the debug information and print out any"
+ desc("Lookup <address> in the debug information and print out any "
"available file, function, block and line table details."),
value_desc("address"), cat(DwarfDumpCategory));
static opt<std::string>
@@ -226,7 +226,7 @@ static alias VerboseAlias("v", desc("Alias for -verbose."), aliasopt(Verbose),
static void error(StringRef Prefix, std::error_code EC) {
if (!EC)
return;
- errs() << Prefix << ": " << EC.message() << "\n";
+ WithColor::error() << Prefix << ": " << EC.message() << "\n";
exit(1);
}
@@ -281,32 +281,45 @@ using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx, Twine,
raw_ostream &)>;
/// Print only DIEs that have a certain name.
+static bool filterByName(const StringSet<> &Names, DWARFDie Die,
+ StringRef NameRef, raw_ostream &OS) {
+ std::string Name =
+ (IgnoreCase && !UseRegex) ? NameRef.lower() : NameRef.str();
+ if (UseRegex) {
+ // Match regular expression.
+ for (auto Pattern : Names.keys()) {
+ Regex RE(Pattern, IgnoreCase ? Regex::IgnoreCase : Regex::NoFlags);
+ std::string Error;
+ if (!RE.isValid(Error)) {
+ errs() << "error in regular expression: " << Error << "\n";
+ exit(1);
+ }
+ if (RE.match(Name)) {
+ Die.dump(OS, 0, getDumpOpts());
+ return true;
+ }
+ }
+ } else if (Names.count(Name)) {
+ // Match full text.
+ Die.dump(OS, 0, getDumpOpts());
+ return true;
+ }
+ return false;
+}
+
+/// Print only DIEs that have a certain name.
static void filterByName(const StringSet<> &Names,
- DWARFContext::cu_iterator_range CUs, raw_ostream &OS) {
+ DWARFContext::unit_iterator_range CUs,
+ raw_ostream &OS) {
for (const auto &CU : CUs)
for (const auto &Entry : CU->dies()) {
DWARFDie Die = {CU.get(), &Entry};
- if (const char *NamePtr = Die.getName(DINameKind::ShortName)) {
- std::string Name =
- (IgnoreCase && !UseRegex) ? StringRef(NamePtr).lower() : NamePtr;
- // Match regular expression.
- if (UseRegex)
- for (auto Pattern : Names.keys()) {
- Regex RE(Pattern, IgnoreCase ? Regex::IgnoreCase : Regex::NoFlags);
- std::string Error;
- if (!RE.isValid(Error)) {
- errs() << "error in regular expression: " << Error << "\n";
- exit(1);
- }
- if (RE.match(Name))
- Die.dump(OS, 0, getDumpOpts());
- }
- // Match full text.
- else if (Names.count(Name))
- Die.dump(OS, 0, getDumpOpts());
- }
+ if (const char *Name = Die.getName(DINameKind::ShortName))
+ if (filterByName(Names, Die, Name, OS))
+ continue;
+ if (const char *Name = Die.getName(DINameKind::LinkageName))
+ filterByName(Names, Die, Name, OS);
}
-
}
static void getDies(DWARFContext &DICtx, const AppleAcceleratorTable &Accel,
@@ -358,7 +371,7 @@ static void filterByAccelName(ArrayRef<std::string> Names, DWARFContext &DICtx,
getDies(DICtx, DICtx.getAppleNamespaces(), Name, Dies);
getDies(DICtx, DICtx.getDebugNames(), Name, Dies);
}
- llvm::sort(Dies.begin(), Dies.end());
+ llvm::sort(Dies);
Dies.erase(std::unique(Dies.begin(), Dies.end()), Dies.end());
for (DWARFDie Die : Dies)
@@ -409,8 +422,8 @@ static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, Twine Filename,
for (auto name : Name)
Names.insert((IgnoreCase && !UseRegex) ? StringRef(name).lower() : name);
- filterByName(Names, DICtx.compile_units(), OS);
- filterByName(Names, DICtx.dwo_compile_units(), OS);
+ filterByName(Names, DICtx.normal_units(), OS);
+ filterByName(Names, DICtx.dwo_units(), OS);
return true;
}
@@ -558,6 +571,14 @@ int main(int argc, char **argv) {
return 0;
}
+ // FIXME: Audit interactions between these two options and make them
+ // compatible.
+ if (Diff && Verbose) {
+ WithColor::error() << "incompatible arguments: specifying both -diff and "
+ "-verbose is currently not supported";
+ return 0;
+ }
+
std::unique_ptr<ToolOutputFile> OutputFile;
if (!OutputFilename.empty()) {
std::error_code EC;
@@ -611,7 +632,7 @@ int main(int argc, char **argv) {
if (Verify) {
// If we encountered errors during verify, exit with a non-zero exit status.
- if (!std::all_of(Objects.begin(), Objects.end(), [&](std::string Object) {
+ if (!all_of(Objects, [&](std::string Object) {
return handleFile(Object, verifyObjectFile, OS);
}))
exit(1);
diff --git a/tools/llvm-dwp/llvm-dwp.cpp b/tools/llvm-dwp/llvm-dwp.cpp
index d3380b5b57a1..8a0744cf1e01 100644
--- a/tools/llvm-dwp/llvm-dwp.cpp
+++ b/tools/llvm-dwp/llvm-dwp.cpp
@@ -43,21 +43,21 @@
using namespace llvm;
using namespace llvm::object;
-using namespace cl;
-OptionCategory DwpCategory("Specific Options");
-static list<std::string> InputFiles(Positional, ZeroOrMore,
- desc("<input files>"), cat(DwpCategory));
+cl::OptionCategory DwpCategory("Specific Options");
+static cl::list<std::string> InputFiles(cl::Positional, cl::ZeroOrMore,
+ cl::desc("<input files>"),
+ cl::cat(DwpCategory));
-static list<std::string> ExecFilenames(
- "e", ZeroOrMore,
- desc("Specify the executable/library files to get the list of *.dwo from"),
- value_desc("filename"), cat(DwpCategory));
+static cl::list<std::string> ExecFilenames(
+ "e", cl::ZeroOrMore,
+ cl::desc("Specify the executable/library files to get the list of *.dwo from"),
+ cl::value_desc("filename"), cl::cat(DwpCategory));
-static opt<std::string> OutputFilename(Required, "o",
- desc("Specify the output file."),
- value_desc("filename"),
- cat(DwpCategory));
+static cl::opt<std::string> OutputFilename(cl::Required, "o",
+ cl::desc("Specify the output file."),
+ cl::value_desc("filename"),
+ cl::cat(DwpCategory));
static void writeStringsAndOffsets(MCStreamer &Out, DWPStringPool &Strings,
MCSection *StrOffsetSection,
@@ -644,7 +644,7 @@ static int error(const Twine &Error, const Twine &Context) {
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
- ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files");
+ cl::ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files\n");
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
@@ -697,13 +697,21 @@ int main(int argc, char **argv) {
// Create the output file.
std::error_code EC;
raw_fd_ostream OutFile(OutputFilename, EC, sys::fs::F_None);
+ Optional<buffer_ostream> BOS;
+ raw_pwrite_stream *OS;
if (EC)
return error(Twine(OutputFilename) + ": " + EC.message(), Context);
+ if (OutFile.supportsSeeking()) {
+ OS = &OutFile;
+ } else {
+ BOS.emplace(OutFile);
+ OS = BOS.getPointer();
+ }
MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags();
std::unique_ptr<MCStreamer> MS(TheTarget->createMCObjectStreamer(
TheTriple, MC, std::unique_ptr<MCAsmBackend>(MAB),
- MAB->createObjectWriter(OutFile), std::unique_ptr<MCCodeEmitter>(MCE),
+ MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(MCE),
*MSTI, MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible,
/*DWARFMustBeAtTheEnd*/ false));
if (!MS)
diff --git a/tools/llvm-elfabi/CMakeLists.txt b/tools/llvm-elfabi/CMakeLists.txt
new file mode 100644
index 000000000000..bd3ec851887a
--- /dev/null
+++ b/tools/llvm-elfabi/CMakeLists.txt
@@ -0,0 +1,11 @@
+set(LLVM_LINK_COMPONENTS
+ Object
+ Support
+ TextAPI
+ )
+
+add_llvm_tool(llvm-elfabi
+ ELFObjHandler.cpp
+ ErrorCollector.cpp
+ llvm-elfabi.cpp
+ )
diff --git a/tools/llvm-elfabi/ELFObjHandler.cpp b/tools/llvm-elfabi/ELFObjHandler.cpp
new file mode 100644
index 000000000000..412c299c1f7d
--- /dev/null
+++ b/tools/llvm-elfabi/ELFObjHandler.cpp
@@ -0,0 +1,68 @@
+//===- ELFObjHandler.cpp --------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-----------------------------------------------------------------------===/
+
+#include "ELFObjHandler.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/ELFTypes.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/TextAPI/ELF/ELFStub.h"
+
+using llvm::MemoryBufferRef;
+using llvm::object::ELFObjectFile;
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::elfabi;
+using namespace llvm::ELF;
+
+namespace llvm {
+namespace elfabi {
+
+/// Returns a new ELFStub with all members populated from an ELFObjectFile.
+/// @param ElfObj Source ELFObjectFile.
+template <class ELFT>
+Expected<std::unique_ptr<ELFStub>>
+buildStub(const ELFObjectFile<ELFT> &ElfObj) {
+ std::unique_ptr<ELFStub> DestStub = make_unique<ELFStub>();
+ const ELFFile<ELFT> *ElfFile = ElfObj.getELFFile();
+
+ DestStub->Arch = ElfFile->getHeader()->e_machine;
+
+ // TODO: Populate SoName from .dynamic entries and linked string table.
+ // TODO: Populate NeededLibs from .dynamic entries and linked string table.
+ // TODO: Populate Symbols from .dynsym table and linked string table.
+
+ return std::move(DestStub);
+}
+
+Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf) {
+ Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Buf);
+ if (!BinOrErr) {
+ return BinOrErr.takeError();
+ }
+
+ Binary *Bin = BinOrErr->get();
+ if (auto Obj = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) {
+ return buildStub(*Obj);
+ } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) {
+ return buildStub(*Obj);
+ } else if (auto Obj = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) {
+ return buildStub(*Obj);
+ } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) {
+ return buildStub(*Obj);
+ }
+
+ return createStringError(errc::not_supported, "Unsupported binary format");
+}
+
+} // end namespace elfabi
+} // end namespace llvm
diff --git a/tools/llvm-elfabi/ELFObjHandler.h b/tools/llvm-elfabi/ELFObjHandler.h
new file mode 100644
index 000000000000..496bad0f9cad
--- /dev/null
+++ b/tools/llvm-elfabi/ELFObjHandler.h
@@ -0,0 +1,33 @@
+//===- ELFObjHandler.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-----------------------------------------------------------------------===/
+///
+/// This supports reading and writing of elf dynamic shared objects.
+///
+//===-----------------------------------------------------------------------===/
+
+#ifndef LLVM_TOOLS_ELFABI_ELFOBJHANDLER_H
+#define LLVM_TOOLS_ELFABI_ELFOBJHANDLER_H
+
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/ELFTypes.h"
+#include "llvm/TextAPI/ELF/ELFStub.h"
+
+namespace llvm {
+
+class MemoryBuffer;
+
+namespace elfabi {
+
+/// Attempt to read a binary ELF file from a MemoryBuffer.
+Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf);
+
+} // end namespace elfabi
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_ELFABI_ELFOBJHANDLER_H
diff --git a/tools/llvm-elfabi/ErrorCollector.cpp b/tools/llvm-elfabi/ErrorCollector.cpp
new file mode 100644
index 000000000000..0d74979c9a30
--- /dev/null
+++ b/tools/llvm-elfabi/ErrorCollector.cpp
@@ -0,0 +1,70 @@
+//===- ErrorCollector.cpp -------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-----------------------------------------------------------------------===/
+
+#include "ErrorCollector.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/WithColor.h"
+#include <vector>
+
+using namespace llvm;
+using namespace llvm::elfabi;
+
+void ErrorCollector::escalateToFatal() {
+ ErrorsAreFatal = true;
+}
+
+void ErrorCollector::addError(Error &&Err, StringRef Tag) {
+ if (Err) {
+ Errors.push_back(std::move(Err));
+ Tags.push_back(Tag.str());
+ }
+}
+
+Error ErrorCollector::makeError() {
+ // TODO: Make this return something (an AggregateError?) that gives more
+ // individual control over each error and which might be of interest.
+ Error JoinedErrors = Error::success();
+ for (Error &E : Errors) {
+ JoinedErrors = joinErrors(std::move(JoinedErrors), std::move(E));
+ }
+ Errors.clear();
+ Tags.clear();
+ return JoinedErrors;
+}
+
+void ErrorCollector::log(raw_ostream &OS) {
+ OS << "Encountered multiple errors:\n";
+ for (size_t i = 0; i < Errors.size(); ++i) {
+ WithColor::error(OS) << "(" << Tags[i] << ") " << Errors[i];
+ if (i != Errors.size() - 1)
+ OS << "\n";
+ }
+}
+
+bool ErrorCollector::allErrorsHandled() const {
+ return Errors.empty();
+}
+
+ErrorCollector::~ErrorCollector() {
+ if (ErrorsAreFatal && !allErrorsHandled())
+ fatalUnhandledError();
+
+ for (Error &E : Errors) {
+ consumeError(std::move(E));
+ }
+}
+
+LLVM_ATTRIBUTE_NORETURN void ErrorCollector::fatalUnhandledError() {
+ errs() << "Program aborted due to unhandled Error(s):\n";
+ log(errs());
+ errs() << "\n";
+ abort();
+}
diff --git a/tools/llvm-elfabi/ErrorCollector.h b/tools/llvm-elfabi/ErrorCollector.h
new file mode 100644
index 000000000000..d54b3fbf4a3c
--- /dev/null
+++ b/tools/llvm-elfabi/ErrorCollector.h
@@ -0,0 +1,75 @@
+//===- ErrorCollector.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-----------------------------------------------------------------------===/
+///
+/// This class collects errors that should be reported or ignored in aggregate.
+///
+/// Like llvm::Error, an ErrorCollector cannot be copied. Unlike llvm::Error,
+/// an ErrorCollector may be destroyed if it was originally constructed to treat
+/// errors as non-fatal. In this case, all Errors are consumed upon destruction.
+/// An ErrorCollector may be initially constructed (or escalated) such that
+/// errors are treated as fatal. This causes a crash if an attempt is made to
+/// delete the ErrorCollector when some Errors have not been retrieved via
+/// makeError().
+///
+//===-----------------------------------------------------------------------===/
+
+#ifndef LLVM_TOOLS_ELFABI_ERRORCOLLECTOR_H
+#define LLVM_TOOLS_ELFABI_ERRORCOLLECTOR_H
+
+#include "llvm/Support/Error.h"
+#include <vector>
+
+namespace llvm {
+namespace elfabi {
+
+class ErrorCollector {
+public:
+ /// Upon destruction, an ErrorCollector will crash if UseFatalErrors=true and
+ /// there are remaining errors that haven't been fetched by makeError().
+ ErrorCollector(bool UseFatalErrors = true) : ErrorsAreFatal(UseFatalErrors) {}
+ // Don't allow copying.
+ ErrorCollector(const ErrorCollector &Stub) = delete;
+ ErrorCollector &operator=(const ErrorCollector &Other) = delete;
+ ~ErrorCollector();
+
+ // TODO: Add move constructor and operator= when a testable situation arises.
+
+ /// Returns a single error that contains messages for all stored Errors.
+ Error makeError();
+
+ /// Adds an error with a descriptive tag that helps with identification.
+ /// If the error is an Error::success(), it is checked and discarded.
+ void addError(Error &&E, StringRef Tag);
+
+ /// This ensures an ErrorCollector will treat unhandled errors as fatal.
+ /// This function should be called if errors that usually can be ignored
+ /// are suddenly of concern (i.e. attempt multiple things that return Error,
+ /// but only care about the Errors if no attempt succeeds).
+ void escalateToFatal();
+
+private:
+ /// Logs all errors to a raw_ostream.
+ void log(raw_ostream &OS);
+
+ /// Returns true if all errors have been retrieved through makeError(), or
+ /// false if errors have been added since the last makeError() call.
+ bool allErrorsHandled() const;
+
+ /// Dump output and crash.
+ LLVM_ATTRIBUTE_NORETURN void fatalUnhandledError();
+
+ bool ErrorsAreFatal;
+ std::vector<Error> Errors;
+ std::vector<std::string> Tags;
+};
+
+} // end namespace elfabi
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_ELFABI_ERRORCOLLECTOR_H
diff --git a/tools/llvm-elfabi/LLVMBuild.txt b/tools/llvm-elfabi/LLVMBuild.txt
new file mode 100644
index 000000000000..e4fdc9ae5a07
--- /dev/null
+++ b/tools/llvm-elfabi/LLVMBuild.txt
@@ -0,0 +1,22 @@
+;===- ./tools/llvm-elfabi/LLVMBuild.txt ------------------------*- Conf -*--===;
+;
+; The LLVM Compiler Infrastructure
+;
+; This file is distributed under the University of Illinois Open Source
+; License. See LICENSE.TXT for details.
+;
+;===------------------------------------------------------------------------===;
+;
+; This is an LLVMBuild description file for the components in this subdirectory.
+;
+; For more information on the LLVMBuild system, please see:
+;
+; http://llvm.org/docs/LLVMBuild.html
+;
+;===------------------------------------------------------------------------===;
+
+[component_0]
+type = Tool
+name = llvm-elfabi
+parent = Tools
+required_libraries = Object Support TextAPI
diff --git a/tools/llvm-elfabi/llvm-elfabi.cpp b/tools/llvm-elfabi/llvm-elfabi.cpp
new file mode 100644
index 000000000000..4c15bc2eaf34
--- /dev/null
+++ b/tools/llvm-elfabi/llvm-elfabi.cpp
@@ -0,0 +1,143 @@
+//===- llvm-elfabi.cpp ----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-----------------------------------------------------------------------===/
+
+#include "ELFObjHandler.h"
+#include "ErrorCollector.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/TextAPI/ELF/TBEHandler.h"
+#include <string>
+
+namespace llvm {
+namespace elfabi {
+
+enum class FileFormat {
+ TBE,
+ ELF
+};
+
+} // end namespace elfabi
+} // end namespace llvm
+
+using namespace llvm;
+using namespace llvm::elfabi;
+
+// Command line flags:
+cl::opt<FileFormat> InputFileFormat(
+ cl::desc("Force input file format:"),
+ cl::values(clEnumValN(FileFormat::TBE,
+ "tbe", "Read `input` as text-based ELF stub"),
+ clEnumValN(FileFormat::ELF,
+ "elf", "Read `input` as ELF binary")));
+cl::opt<std::string> InputFilePath(cl::Positional, cl::desc("input"),
+ cl::Required);
+cl::opt<std::string>
+ EmitTBE("emit-tbe",
+ cl::desc("Emit a text-based ELF stub (.tbe) from the input file"),
+ cl::value_desc("path"));
+cl::opt<std::string> SOName(
+ "soname",
+ cl::desc("Manually set the DT_SONAME entry of any emitted files"),
+ cl::value_desc("name"));
+
+/// writeTBE() writes a Text-Based ELF stub to a file using the latest version
+/// of the YAML parser.
+static Error writeTBE(StringRef FilePath, ELFStub &Stub) {
+ std::error_code SysErr;
+
+ // Open file for writing.
+ raw_fd_ostream Out(FilePath, SysErr);
+ if (SysErr)
+ return createStringError(SysErr, "Couldn't open `%s` for writing",
+ FilePath.data());
+ // Write file.
+ Error YAMLErr = writeTBEToOutputStream(Out, Stub);
+ if (YAMLErr)
+ return YAMLErr;
+
+ return Error::success();
+}
+
+/// readInputFile populates an ELFStub by attempting to read the
+/// input file using both the TBE and binary ELF parsers.
+static Expected<std::unique_ptr<ELFStub>> readInputFile(StringRef FilePath) {
+ // Read in file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
+ MemoryBuffer::getFile(FilePath);
+ if (!BufOrError) {
+ return createStringError(BufOrError.getError(), "Could not open `%s`",
+ FilePath.data());
+ }
+
+ std::unique_ptr<MemoryBuffer> FileReadBuffer = std::move(*BufOrError);
+ ErrorCollector EC(/*UseFatalErrors=*/false);
+
+ // First try to read as a binary (fails fast if not binary).
+ if (InputFileFormat.getNumOccurrences() == 0 ||
+ InputFileFormat == FileFormat::ELF) {
+ Expected<std::unique_ptr<ELFStub>> StubFromELF =
+ readELFFile(FileReadBuffer->getMemBufferRef());
+ if (StubFromELF) {
+ return std::move(*StubFromELF);
+ }
+ EC.addError(StubFromELF.takeError(), "BinaryRead");
+ }
+
+ // Fall back to reading as a tbe.
+ if (InputFileFormat.getNumOccurrences() == 0 ||
+ InputFileFormat == FileFormat::TBE) {
+ Expected<std::unique_ptr<ELFStub>> StubFromTBE =
+ readTBEFromBuffer(FileReadBuffer->getBuffer());
+ if (StubFromTBE) {
+ return std::move(*StubFromTBE);
+ }
+ EC.addError(StubFromTBE.takeError(), "YamlParse");
+ }
+
+ // If both readers fail, build a new error that includes all information.
+ EC.addError(createStringError(errc::not_supported,
+ "No file readers succeeded reading `%s` "
+ "(unsupported/malformed file?)",
+ FilePath.data()),
+ "ReadInputFile");
+ EC.escalateToFatal();
+ return EC.makeError();
+}
+
+int main(int argc, char *argv[]) {
+ // Parse arguments.
+ cl::ParseCommandLineOptions(argc, argv);
+
+ Expected<std::unique_ptr<ELFStub>> StubOrErr = readInputFile(InputFilePath);
+ if (!StubOrErr) {
+ Error ReadError = StubOrErr.takeError();
+ WithColor::error() << ReadError << "\n";
+ exit(1);
+ }
+
+ std::unique_ptr<ELFStub> TargetStub = std::move(StubOrErr.get());
+
+ // Write out .tbe file.
+ if (EmitTBE.getNumOccurrences() == 1) {
+ TargetStub->TbeVersion = TBEVersionCurrent;
+ if (SOName.getNumOccurrences() == 1) {
+ TargetStub->SoName = SOName;
+ }
+ Error TBEWriteError = writeTBE(EmitTBE, *TargetStub);
+ if (TBEWriteError) {
+ WithColor::error() << TBEWriteError << "\n";
+ exit(1);
+ }
+ }
+}
diff --git a/tools/llvm-exegesis/CMakeLists.txt b/tools/llvm-exegesis/CMakeLists.txt
index 65b1ada85299..a59e1b74024e 100644
--- a/tools/llvm-exegesis/CMakeLists.txt
+++ b/tools/llvm-exegesis/CMakeLists.txt
@@ -1,5 +1,5 @@
-
set(LLVM_LINK_COMPONENTS
+ MCParser
Support
native
)
diff --git a/tools/llvm-exegesis/lib/AArch64/Target.cpp b/tools/llvm-exegesis/lib/AArch64/Target.cpp
index 88df44bd94c2..d6b6cff54727 100644
--- a/tools/llvm-exegesis/lib/AArch64/Target.cpp
+++ b/tools/llvm-exegesis/lib/AArch64/Target.cpp
@@ -9,35 +9,59 @@
#include "../Target.h"
#include "../Latency.h"
#include "AArch64.h"
+#include "AArch64RegisterInfo.h"
+namespace llvm {
namespace exegesis {
+static unsigned getLoadImmediateOpcode(unsigned RegBitWidth) {
+ switch (RegBitWidth) {
+ case 32:
+ return llvm::AArch64::MOVi32imm;
+ case 64:
+ return llvm::AArch64::MOVi64imm;
+ }
+ llvm_unreachable("Invalid Value Width");
+}
+
+// Generates instruction to load an immediate value into a register.
+static llvm::MCInst loadImmediate(unsigned Reg, unsigned RegBitWidth,
+ const llvm::APInt &Value) {
+ if (Value.getBitWidth() > RegBitWidth)
+ llvm_unreachable("Value must fit in the Register");
+ return llvm::MCInstBuilder(getLoadImmediateOpcode(RegBitWidth))
+ .addReg(Reg)
+ .addImm(Value.getZExtValue());
+}
+
+#include "AArch64GenExegesis.inc"
+
namespace {
-class AArch64LatencyBenchmarkRunner : public LatencyBenchmarkRunner {
+class ExegesisAArch64Target : public ExegesisTarget {
public:
- AArch64LatencyBenchmarkRunner(const LLVMState &State)
- : LatencyBenchmarkRunner(State) {}
+ ExegesisAArch64Target() : ExegesisTarget(AArch64CpuPfmCounters) {}
private:
- const char *getCounterName() const override {
- // All AArch64 subtargets have CPU_CYCLES as the cycle counter name
- return "CPU_CYCLES";
+ std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI,
+ unsigned Reg,
+ const llvm::APInt &Value) const override {
+ if (llvm::AArch64::GPR32RegClass.contains(Reg))
+ return {loadImmediate(Reg, 32, Value)};
+ if (llvm::AArch64::GPR64RegClass.contains(Reg))
+ return {loadImmediate(Reg, 64, Value)};
+ llvm::errs() << "setRegTo is not implemented, results will be unreliable\n";
+ return {};
}
-};
-class ExegesisAArch64Target : public ExegesisTarget {
bool matchesArch(llvm::Triple::ArchType Arch) const override {
return Arch == llvm::Triple::aarch64 || Arch == llvm::Triple::aarch64_be;
}
+
void addTargetSpecificPasses(llvm::PassManagerBase &PM) const override {
// Function return is a pseudo-instruction that needs to be expanded
PM.add(llvm::createAArch64ExpandPseudoPass());
}
- std::unique_ptr<BenchmarkRunner>
- createLatencyBenchmarkRunner(const LLVMState &State) const override {
- return llvm::make_unique<AArch64LatencyBenchmarkRunner>(State);
- }
};
} // namespace
@@ -52,3 +76,4 @@ void InitializeAArch64ExegesisTarget() {
}
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/Analysis.cpp b/tools/llvm-exegesis/lib/Analysis.cpp
index bb5118080967..0a91679fe1d1 100644
--- a/tools/llvm-exegesis/lib/Analysis.cpp
+++ b/tools/llvm-exegesis/lib/Analysis.cpp
@@ -12,13 +12,25 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Support/FormatVariadic.h"
+#include <limits>
#include <unordered_set>
#include <vector>
+namespace llvm {
namespace exegesis {
static const char kCsvSep = ',';
+static unsigned resolveSchedClassId(const llvm::MCSubtargetInfo &STI,
+ unsigned SchedClassId,
+ const llvm::MCInst &MCI) {
+ const auto &SM = STI.getSchedModel();
+ while (SchedClassId && SM.getSchedClassDesc(SchedClassId)->isVariant())
+ SchedClassId =
+ STI.resolveVariantSchedClass(SchedClassId, &MCI, SM.getProcessorID());
+ return SchedClassId;
+}
+
namespace {
enum EscapeTag { kEscapeCsv, kEscapeHtml, kEscapeHtmlString };
@@ -84,7 +96,21 @@ writeClusterId(llvm::raw_ostream &OS,
template <EscapeTag Tag>
static void writeMeasurementValue(llvm::raw_ostream &OS, const double Value) {
- writeEscaped<Tag>(OS, llvm::formatv("{0:F}", Value).str());
+ // Given Value, if we wanted to serialize it to a string,
+ // how many base-10 digits will we need to store, max?
+ static constexpr auto MaxDigitCount =
+ std::numeric_limits<decltype(Value)>::max_digits10;
+ // Also, we will need a decimal separator.
+ static constexpr auto DecimalSeparatorLen = 1; // '.' e.g.
+ // So how long of a string will the serialization produce, max?
+ static constexpr auto SerializationLen = MaxDigitCount + DecimalSeparatorLen;
+
+ // WARNING: when changing the format, also adjust the small-size estimate ^.
+ static constexpr StringLiteral SimpleFloatFormat = StringLiteral("{0:F}");
+
+ writeEscaped<Tag>(
+ OS,
+ llvm::formatv(SimpleFloatFormat.data(), Value).sstr<SerializationLen>());
}
template <typename EscapeTag, EscapeTag Tag>
@@ -103,13 +129,11 @@ void Analysis::writeSnippet(llvm::raw_ostream &OS,
writeEscaped<Tag>(OS, "[error decoding asm snippet]");
return;
}
- Lines.emplace_back();
- std::string &Line = Lines.back();
- llvm::raw_string_ostream OSS(Line);
+ llvm::SmallString<128> InstPrinterStr; // FIXME: magic number.
+ llvm::raw_svector_ostream OSS(InstPrinterStr);
InstPrinter_->printInst(&MI, OSS, "", *SubtargetInfo_);
Bytes = Bytes.drop_front(MISize);
- OSS.flush();
- Line = llvm::StringRef(Line).trim().str();
+ Lines.emplace_back(llvm::StringRef(InstPrinterStr).trim());
}
writeEscaped<Tag>(OS, llvm::join(Lines, Separator));
}
@@ -126,20 +150,20 @@ void Analysis::printInstructionRowCsv(const size_t PointId,
writeEscaped<kEscapeCsv>(OS, Point.Key.Config);
OS << kCsvSep;
assert(!Point.Key.Instructions.empty());
- // FIXME: Resolve variant classes.
- const unsigned SchedClassId =
- InstrInfo_->get(Point.Key.Instructions[0].getOpcode()).getSchedClass();
+ const llvm::MCInst &MCI = Point.Key.Instructions[0];
+ const unsigned SchedClassId = resolveSchedClassId(
+ *SubtargetInfo_, InstrInfo_->get(MCI.getOpcode()).getSchedClass(), MCI);
+
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- const auto &SchedModel = SubtargetInfo_->getSchedModel();
const llvm::MCSchedClassDesc *const SCDesc =
- SchedModel.getSchedClassDesc(SchedClassId);
+ SubtargetInfo_->getSchedModel().getSchedClassDesc(SchedClassId);
writeEscaped<kEscapeCsv>(OS, SCDesc->Name);
#else
OS << SchedClassId;
#endif
for (const auto &Measurement : Point.Measurements) {
OS << kCsvSep;
- writeMeasurementValue<kEscapeCsv>(OS, Measurement.Value);
+ writeMeasurementValue<kEscapeCsv>(OS, Measurement.PerInstructionValue);
}
OS << "\n";
}
@@ -193,21 +217,43 @@ Analysis::run<Analysis::PrintClusters>(llvm::raw_ostream &OS) const {
return llvm::Error::success();
}
-std::unordered_map<unsigned, std::vector<size_t>>
+Analysis::ResolvedSchedClassAndPoints::ResolvedSchedClassAndPoints(
+ ResolvedSchedClass &&RSC)
+ : RSC(std::move(RSC)) {}
+
+std::vector<Analysis::ResolvedSchedClassAndPoints>
Analysis::makePointsPerSchedClass() const {
- std::unordered_map<unsigned, std::vector<size_t>> PointsPerSchedClass;
+ std::vector<ResolvedSchedClassAndPoints> Entries;
+ // Maps SchedClassIds to index in result.
+ std::unordered_map<unsigned, size_t> SchedClassIdToIndex;
const auto &Points = Clustering_.getPoints();
for (size_t PointId = 0, E = Points.size(); PointId < E; ++PointId) {
const InstructionBenchmark &Point = Points[PointId];
if (!Point.Error.empty())
continue;
assert(!Point.Key.Instructions.empty());
- const auto Opcode = Point.Key.Instructions[0].getOpcode();
- // FIXME: Resolve variant classes.
- PointsPerSchedClass[InstrInfo_->get(Opcode).getSchedClass()].push_back(
- PointId);
+ // FIXME: we should be using the tuple of classes for instructions in the
+ // snippet as key.
+ const llvm::MCInst &MCI = Point.Key.Instructions[0];
+ unsigned SchedClassId = InstrInfo_->get(MCI.getOpcode()).getSchedClass();
+ const bool WasVariant = SchedClassId && SubtargetInfo_->getSchedModel()
+ .getSchedClassDesc(SchedClassId)
+ ->isVariant();
+ SchedClassId = resolveSchedClassId(*SubtargetInfo_, SchedClassId, MCI);
+ const auto IndexIt = SchedClassIdToIndex.find(SchedClassId);
+ if (IndexIt == SchedClassIdToIndex.end()) {
+ // Create a new entry.
+ SchedClassIdToIndex.emplace(SchedClassId, Entries.size());
+ ResolvedSchedClassAndPoints Entry(
+ ResolvedSchedClass(*SubtargetInfo_, SchedClassId, WasVariant));
+ Entry.PointIds.push_back(PointId);
+ Entries.push_back(std::move(Entry));
+ } else {
+ // Append to the existing entry.
+ Entries[IndexIt->second].PointIds.push_back(PointId);
+ }
}
- return PointsPerSchedClass;
+ return Entries;
}
// Uops repeat the same opcode over again. Just show this opcode and show the
@@ -239,8 +285,8 @@ writeLatencySnippetHtml(llvm::raw_ostream &OS,
}
void Analysis::printSchedClassClustersHtml(
- const std::vector<SchedClassCluster> &Clusters, const SchedClass &SC,
- llvm::raw_ostream &OS) const {
+ const std::vector<SchedClassCluster> &Clusters,
+ const ResolvedSchedClass &RSC, llvm::raw_ostream &OS) const {
const auto &Points = Clustering_.getPoints();
OS << "<table class=\"sched-class-clusters\">";
OS << "<tr><th>ClusterId</th><th>Opcode/Config</th>";
@@ -248,16 +294,13 @@ void Analysis::printSchedClassClustersHtml(
for (const auto &Measurement :
Points[Clusters[0].getPointIds()[0]].Measurements) {
OS << "<th>";
- if (Measurement.DebugString.empty())
- writeEscaped<kEscapeHtml>(OS, Measurement.Key);
- else
- writeEscaped<kEscapeHtml>(OS, Measurement.DebugString);
+ writeEscaped<kEscapeHtml>(OS, Measurement.Key);
OS << "</th>";
}
OS << "</tr>";
for (const SchedClassCluster &Cluster : Clusters) {
OS << "<tr class=\""
- << (Cluster.measurementsMatch(*SubtargetInfo_, SC, Clustering_)
+ << (Cluster.measurementsMatch(*SubtargetInfo_, RSC, Clustering_)
? "good-cluster"
: "bad-cluster")
<< "\"><td>";
@@ -372,12 +415,17 @@ getNonRedundantWriteProcRes(const llvm::MCSchedClassDesc &SCDesc,
return Result;
}
-Analysis::SchedClass::SchedClass(const llvm::MCSchedClassDesc &SD,
- const llvm::MCSubtargetInfo &STI)
- : SCDesc(&SD),
- NonRedundantWriteProcRes(getNonRedundantWriteProcRes(SD, STI)),
+Analysis::ResolvedSchedClass::ResolvedSchedClass(
+ const llvm::MCSubtargetInfo &STI, unsigned ResolvedSchedClassId,
+ bool WasVariant)
+ : SchedClassId(ResolvedSchedClassId), SCDesc(STI.getSchedModel().getSchedClassDesc(ResolvedSchedClassId)),
+ WasVariant(WasVariant),
+ NonRedundantWriteProcRes(getNonRedundantWriteProcRes(*SCDesc, STI)),
IdealizedProcResPressure(computeIdealizedProcResPressure(
- STI.getSchedModel(), NonRedundantWriteProcRes)) {}
+ STI.getSchedModel(), NonRedundantWriteProcRes)) {
+ assert((SCDesc == nullptr || !SCDesc->isVariant()) &&
+ "ResolvedSchedClass should never be variant");
+}
void Analysis::SchedClassCluster::addPoint(
size_t PointId, const InstructionBenchmarkClustering &Clustering) {
@@ -393,8 +441,24 @@ void Analysis::SchedClassCluster::addPoint(
assert(ClusterId == Clustering.getClusterIdForPoint(PointId));
}
+// Returns a ProxResIdx by id or name.
+static unsigned findProcResIdx(const llvm::MCSubtargetInfo &STI,
+ const llvm::StringRef NameOrId) {
+ // Interpret the key as an ProcResIdx.
+ unsigned ProcResIdx = 0;
+ if (llvm::to_integer(NameOrId, ProcResIdx, 10))
+ return ProcResIdx;
+ // Interpret the key as a ProcRes name.
+ const auto &SchedModel = STI.getSchedModel();
+ for (int I = 0, E = SchedModel.getNumProcResourceKinds(); I < E; ++I) {
+ if (NameOrId == SchedModel.getProcResource(I)->Name)
+ return I;
+ }
+ return 0;
+}
+
bool Analysis::SchedClassCluster::measurementsMatch(
- const llvm::MCSubtargetInfo &STI, const SchedClass &SC,
+ const llvm::MCSubtargetInfo &STI, const ResolvedSchedClass &RSC,
const InstructionBenchmarkClustering &Clustering) const {
const size_t NumMeasurements = Representative.size();
std::vector<BenchmarkMeasure> ClusterCenterPoint(NumMeasurements);
@@ -410,34 +474,39 @@ bool Analysis::SchedClassCluster::measurementsMatch(
return false;
}
// Find the latency.
- SchedClassPoint[0].Value = 0.0;
- for (unsigned I = 0; I < SC.SCDesc->NumWriteLatencyEntries; ++I) {
+ SchedClassPoint[0].PerInstructionValue = 0.0;
+ for (unsigned I = 0; I < RSC.SCDesc->NumWriteLatencyEntries; ++I) {
const llvm::MCWriteLatencyEntry *const WLE =
- STI.getWriteLatencyEntry(SC.SCDesc, I);
- SchedClassPoint[0].Value =
- std::max<double>(SchedClassPoint[0].Value, WLE->Cycles);
+ STI.getWriteLatencyEntry(RSC.SCDesc, I);
+ SchedClassPoint[0].PerInstructionValue =
+ std::max<double>(SchedClassPoint[0].PerInstructionValue, WLE->Cycles);
}
- ClusterCenterPoint[0].Value = Representative[0].avg();
+ ClusterCenterPoint[0].PerInstructionValue = Representative[0].avg();
} else if (Mode == InstructionBenchmark::Uops) {
for (int I = 0, E = Representative.size(); I < E; ++I) {
- // Find the pressure on ProcResIdx `Key`.
- uint16_t ProcResIdx = 0;
- if (!llvm::to_integer(Representative[I].key(), ProcResIdx, 10)) {
- llvm::errs() << "expected ProcResIdx key, got "
- << Representative[I].key() << "\n";
+ const auto Key = Representative[I].key();
+ uint16_t ProcResIdx = findProcResIdx(STI, Key);
+ if (ProcResIdx > 0) {
+ // Find the pressure on ProcResIdx `Key`.
+ const auto ProcResPressureIt =
+ std::find_if(RSC.IdealizedProcResPressure.begin(),
+ RSC.IdealizedProcResPressure.end(),
+ [ProcResIdx](const std::pair<uint16_t, float> &WPR) {
+ return WPR.first == ProcResIdx;
+ });
+ SchedClassPoint[I].PerInstructionValue =
+ ProcResPressureIt == RSC.IdealizedProcResPressure.end()
+ ? 0.0
+ : ProcResPressureIt->second;
+ } else if (Key == "NumMicroOps") {
+ SchedClassPoint[I].PerInstructionValue = RSC.SCDesc->NumMicroOps;
+ } else {
+ llvm::errs() << "expected `key` to be either a ProcResIdx or a ProcRes "
+ "name, got "
+ << Key << "\n";
return false;
}
- const auto ProcResPressureIt =
- std::find_if(SC.IdealizedProcResPressure.begin(),
- SC.IdealizedProcResPressure.end(),
- [ProcResIdx](const std::pair<uint16_t, float> &WPR) {
- return WPR.first == ProcResIdx;
- });
- SchedClassPoint[I].Value =
- ProcResPressureIt == SC.IdealizedProcResPressure.end()
- ? 0.0
- : ProcResPressureIt->second;
- ClusterCenterPoint[I].Value = Representative[I].avg();
+ ClusterCenterPoint[I].PerInstructionValue = Representative[I].avg();
}
} else {
llvm::errs() << "unimplemented measurement matching for mode " << Mode
@@ -447,26 +516,25 @@ bool Analysis::SchedClassCluster::measurementsMatch(
return Clustering.isNeighbour(ClusterCenterPoint, SchedClassPoint);
}
-void Analysis::printSchedClassDescHtml(const SchedClass &SC,
+void Analysis::printSchedClassDescHtml(const ResolvedSchedClass &RSC,
llvm::raw_ostream &OS) const {
OS << "<table class=\"sched-class-desc\">";
- OS << "<tr><th>Valid</th><th>Variant</th><th>uOps</th><th>Latency</"
+ OS << "<tr><th>Valid</th><th>Variant</th><th>NumMicroOps</th><th>Latency</"
"th><th>WriteProcRes</th><th title=\"This is the idealized unit "
"resource (port) pressure assuming ideal distribution\">Idealized "
"Resource Pressure</th></tr>";
- if (SC.SCDesc->isValid()) {
+ if (RSC.SCDesc->isValid()) {
const auto &SM = SubtargetInfo_->getSchedModel();
OS << "<tr><td>&#10004;</td>";
- OS << "<td>" << (SC.SCDesc->isVariant() ? "&#10004;" : "&#10005;")
- << "</td>";
- OS << "<td>" << SC.SCDesc->NumMicroOps << "</td>";
+ OS << "<td>" << (RSC.WasVariant ? "&#10004;" : "&#10005;") << "</td>";
+ OS << "<td>" << RSC.SCDesc->NumMicroOps << "</td>";
// Latencies.
OS << "<td><ul>";
- for (int I = 0, E = SC.SCDesc->NumWriteLatencyEntries; I < E; ++I) {
+ for (int I = 0, E = RSC.SCDesc->NumWriteLatencyEntries; I < E; ++I) {
const auto *const Entry =
- SubtargetInfo_->getWriteLatencyEntry(SC.SCDesc, I);
+ SubtargetInfo_->getWriteLatencyEntry(RSC.SCDesc, I);
OS << "<li>" << Entry->Cycles;
- if (SC.SCDesc->NumWriteLatencyEntries > 1) {
+ if (RSC.SCDesc->NumWriteLatencyEntries > 1) {
// Dismabiguate if more than 1 latency.
OS << " (WriteResourceID " << Entry->WriteResourceID << ")";
}
@@ -475,7 +543,7 @@ void Analysis::printSchedClassDescHtml(const SchedClass &SC,
OS << "</ul></td>";
// WriteProcRes.
OS << "<td><ul>";
- for (const auto &WPR : SC.NonRedundantWriteProcRes) {
+ for (const auto &WPR : RSC.NonRedundantWriteProcRes) {
OS << "<li><span class=\"mono\">";
writeEscaped<kEscapeHtml>(OS,
SM.getProcResource(WPR.ProcResourceIdx)->Name);
@@ -484,7 +552,7 @@ void Analysis::printSchedClassDescHtml(const SchedClass &SC,
OS << "</ul></td>";
// Idealized port pressure.
OS << "<td><ul>";
- for (const auto &Pressure : SC.IdealizedProcResPressure) {
+ for (const auto &Pressure : RSC.IdealizedProcResPressure) {
OS << "<li><span class=\"mono\">";
writeEscaped<kEscapeHtml>(OS, SubtargetInfo_->getSchedModel()
.getProcResource(Pressure.first)
@@ -580,19 +648,12 @@ llvm::Error Analysis::run<Analysis::PrintSchedClassInconsistencies>(
writeEscaped<kEscapeHtml>(OS, FirstPoint.CpuName);
OS << "</span></h3>";
- for (const auto &SchedClassAndPoints : makePointsPerSchedClass()) {
- const auto SchedClassId = SchedClassAndPoints.first;
- const std::vector<size_t> &SchedClassPoints = SchedClassAndPoints.second;
- const auto &SchedModel = SubtargetInfo_->getSchedModel();
- const llvm::MCSchedClassDesc *const SCDesc =
- SchedModel.getSchedClassDesc(SchedClassId);
- if (!SCDesc)
+ for (const auto &RSCAndPoints : makePointsPerSchedClass()) {
+ if (!RSCAndPoints.RSC.SCDesc)
continue;
- const SchedClass SC(*SCDesc, *SubtargetInfo_);
-
// Bucket sched class points into sched class clusters.
std::vector<SchedClassCluster> SchedClassClusters;
- for (const size_t PointId : SchedClassPoints) {
+ for (const size_t PointId : RSCAndPoints.PointIds) {
const auto &ClusterId = Clustering_.getClusterIdForPoint(PointId);
if (!ClusterId.isValid())
continue; // Ignore noise and errors. FIXME: take noise into account ?
@@ -610,25 +671,25 @@ llvm::Error Analysis::run<Analysis::PrintSchedClassInconsistencies>(
// Print any scheduling class that has at least one cluster that does not
// match the checked-in data.
- if (std::all_of(SchedClassClusters.begin(), SchedClassClusters.end(),
- [this, &SC](const SchedClassCluster &C) {
- return C.measurementsMatch(*SubtargetInfo_, SC,
- Clustering_);
- }))
+ if (llvm::all_of(SchedClassClusters,
+ [this, &RSCAndPoints](const SchedClassCluster &C) {
+ return C.measurementsMatch(
+ *SubtargetInfo_, RSCAndPoints.RSC, Clustering_);
+ }))
continue; // Nothing weird.
OS << "<div class=\"inconsistency\"><p>Sched Class <span "
"class=\"sched-class-name\">";
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
- writeEscaped<kEscapeHtml>(OS, SCDesc->Name);
+ writeEscaped<kEscapeHtml>(OS, RSCAndPoints.RSC.SCDesc->Name);
#else
- OS << SchedClassId;
+ OS << RSCAndPoints.RSC.SchedClassId;
#endif
OS << "</span> contains instructions whose performance characteristics do"
" not match that of LLVM:</p>";
- printSchedClassClustersHtml(SchedClassClusters, SC, OS);
+ printSchedClassClustersHtml(SchedClassClusters, RSCAndPoints.RSC, OS);
OS << "<p>llvm SchedModel data:</p>";
- printSchedClassDescHtml(SC, OS);
+ printSchedClassDescHtml(RSCAndPoints.RSC, OS);
OS << "</div>";
}
@@ -671,10 +732,9 @@ void distributePressure(float RemainingPressure,
llvm::SmallVector<float, 32> &DensePressure) {
// Find the number of subunits with minimal pressure (they are at the
// front).
- llvm::sort(Subunits.begin(), Subunits.end(),
- [&DensePressure](const uint16_t A, const uint16_t B) {
- return DensePressure[A] < DensePressure[B];
- });
+ llvm::sort(Subunits, [&DensePressure](const uint16_t A, const uint16_t B) {
+ return DensePressure[A] < DensePressure[B];
+ });
const auto getPressureForSubunit = [&DensePressure,
&Subunits](size_t I) -> float & {
return DensePressure[Subunits[I]];
@@ -721,11 +781,10 @@ std::vector<std::pair<uint16_t, float>> computeIdealizedProcResPressure(
llvm::SmallVector<llvm::MCWriteProcResEntry, 8> WPRS) {
// DensePressure[I] is the port pressure for Proc Resource I.
llvm::SmallVector<float, 32> DensePressure(SM.getNumProcResourceKinds());
- llvm::sort(WPRS.begin(), WPRS.end(),
- [](const llvm::MCWriteProcResEntry &A,
- const llvm::MCWriteProcResEntry &B) {
- return A.ProcResourceIdx < B.ProcResourceIdx;
- });
+ llvm::sort(WPRS, [](const llvm::MCWriteProcResEntry &A,
+ const llvm::MCWriteProcResEntry &B) {
+ return A.ProcResourceIdx < B.ProcResourceIdx;
+ });
for (const llvm::MCWriteProcResEntry &WPR : WPRS) {
// Get units for the entry.
const llvm::MCProcResourceDesc *const ProcResDesc =
@@ -751,3 +810,4 @@ std::vector<std::pair<uint16_t, float>> computeIdealizedProcResPressure(
}
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/Analysis.h b/tools/llvm-exegesis/lib/Analysis.h
index 3c33cafc5fb9..9ee1493f4e05 100644
--- a/tools/llvm-exegesis/lib/Analysis.h
+++ b/tools/llvm-exegesis/lib/Analysis.h
@@ -30,6 +30,7 @@
#include <string>
#include <unordered_map>
+namespace llvm {
namespace exegesis {
// A helper class to analyze benchmark results for a target.
@@ -49,11 +50,13 @@ private:
using ClusterId = InstructionBenchmarkClustering::ClusterId;
// An llvm::MCSchedClassDesc augmented with some additional data.
- struct SchedClass {
- SchedClass(const llvm::MCSchedClassDesc &SD,
- const llvm::MCSubtargetInfo &STI);
+ struct ResolvedSchedClass {
+ ResolvedSchedClass(const llvm::MCSubtargetInfo &STI,
+ unsigned ResolvedSchedClassId, bool WasVariant);
+ const unsigned SchedClassId;
const llvm::MCSchedClassDesc *const SCDesc;
+ const bool WasVariant; // Whether the original class was variant.
const llvm::SmallVector<llvm::MCWriteProcResEntry, 8>
NonRedundantWriteProcRes;
const std::vector<std::pair<uint16_t, float>> IdealizedProcResPressure;
@@ -69,13 +72,14 @@ private:
const std::vector<size_t> &getPointIds() const { return PointIds; }
// Return the cluster centroid.
- const std::vector<BenchmarkMeasureStats> &getRepresentative() const {
+ const std::vector<PerInstructionStats> &getRepresentative() const {
return Representative;
}
// Returns true if the cluster representative measurements match that of SC.
bool
- measurementsMatch(const llvm::MCSubtargetInfo &STI, const SchedClass &SC,
+ measurementsMatch(const llvm::MCSubtargetInfo &STI,
+ const ResolvedSchedClass &SC,
const InstructionBenchmarkClustering &Clustering) const;
void addPoint(size_t PointId,
@@ -85,22 +89,29 @@ private:
InstructionBenchmarkClustering::ClusterId ClusterId;
std::vector<size_t> PointIds;
// Measurement stats for the points in the SchedClassCluster.
- std::vector<BenchmarkMeasureStats> Representative;
+ std::vector<PerInstructionStats> Representative;
};
void printInstructionRowCsv(size_t PointId, llvm::raw_ostream &OS) const;
void
printSchedClassClustersHtml(const std::vector<SchedClassCluster> &Clusters,
- const SchedClass &SC,
+ const ResolvedSchedClass &SC,
llvm::raw_ostream &OS) const;
- void printSchedClassDescHtml(const SchedClass &SC,
+ void printSchedClassDescHtml(const ResolvedSchedClass &SC,
llvm::raw_ostream &OS) const;
- // Builds a map of Sched Class -> indices of points that belong to the sched
- // class.
- std::unordered_map<unsigned, std::vector<size_t>>
- makePointsPerSchedClass() const;
+ // A pair of (Sched Class, indices of points that belong to the sched
+ // class).
+ struct ResolvedSchedClassAndPoints {
+ explicit ResolvedSchedClassAndPoints(ResolvedSchedClass &&RSC);
+
+ ResolvedSchedClass RSC;
+ std::vector<size_t> PointIds;
+ };
+
+ // Builds a list of ResolvedSchedClassAndPoints.
+ std::vector<ResolvedSchedClassAndPoints> makePointsPerSchedClass() const;
template <typename EscapeTag, EscapeTag Tag>
void writeSnippet(llvm::raw_ostream &OS, llvm::ArrayRef<uint8_t> Bytes,
@@ -125,5 +136,6 @@ std::vector<std::pair<uint16_t, float>> computeIdealizedProcResPressure(
llvm::SmallVector<llvm::MCWriteProcResEntry, 8> WPRS);
} // namespace exegesis
+} // namespace llvm
#endif // LLVM_TOOLS_LLVM_EXEGESIS_CLUSTERING_H
diff --git a/tools/llvm-exegesis/lib/Assembler.cpp b/tools/llvm-exegesis/lib/Assembler.cpp
index d2be7f4829a7..2e3712ce7dc7 100644
--- a/tools/llvm-exegesis/lib/Assembler.cpp
+++ b/tools/llvm-exegesis/lib/Assembler.cpp
@@ -23,23 +23,25 @@
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/Support/MemoryBuffer.h"
+namespace llvm {
namespace exegesis {
static constexpr const char ModuleID[] = "ExegesisInfoTest";
static constexpr const char FunctionID[] = "foo";
static std::vector<llvm::MCInst>
-generateSnippetSetupCode(const llvm::ArrayRef<unsigned> RegsToDef,
- const ExegesisTarget &ET,
- const llvm::LLVMTargetMachine &TM, bool &IsComplete) {
- IsComplete = true;
+generateSnippetSetupCode(const ExegesisTarget &ET,
+ const llvm::MCSubtargetInfo *const MSI,
+ llvm::ArrayRef<RegisterValue> RegisterInitialValues,
+ bool &IsSnippetSetupComplete) {
+ IsSnippetSetupComplete = true;
std::vector<llvm::MCInst> Result;
- for (const unsigned Reg : RegsToDef) {
+ for (const RegisterValue &RV : RegisterInitialValues) {
// Load a constant in the register.
- const auto Code = ET.setRegToConstant(*TM.getMCSubtargetInfo(), Reg);
- if (Code.empty())
- IsComplete = false;
- Result.insert(Result.end(), Code.begin(), Code.end());
+ const auto SetRegisterCode = ET.setRegTo(*MSI, RV.Register, RV.Value);
+ if (SetRegisterCode.empty())
+ IsSnippetSetupComplete = false;
+ Result.insert(Result.end(), SetRegisterCode.begin(), SetRegisterCode.end());
}
return Result;
}
@@ -66,12 +68,16 @@ static bool addPass(llvm::PassManagerBase &PM, llvm::StringRef PassName,
return false;
}
-// Creates a void MachineFunction with no argument.
+// Creates a void(int8*) MachineFunction.
static llvm::MachineFunction &
-createVoidVoidMachineFunction(llvm::StringRef FunctionID, llvm::Module *Module,
- llvm::MachineModuleInfo *MMI) {
+createVoidVoidPtrMachineFunction(llvm::StringRef FunctionID,
+ llvm::Module *Module,
+ llvm::MachineModuleInfo *MMI) {
llvm::Type *const ReturnType = llvm::Type::getInt32Ty(Module->getContext());
- llvm::FunctionType *FunctionType = llvm::FunctionType::get(ReturnType, false);
+ llvm::Type *const MemParamType = llvm::PointerType::get(
+ llvm::Type::getInt8Ty(Module->getContext()), 0 /*default address space*/);
+ llvm::FunctionType *FunctionType =
+ llvm::FunctionType::get(ReturnType, {MemParamType}, false);
llvm::Function *const F = llvm::Function::Create(
FunctionType, llvm::GlobalValue::InternalLinkage, FunctionID, Module);
// Making sure we can create a MachineFunction out of this Function even if it
@@ -81,9 +87,12 @@ createVoidVoidMachineFunction(llvm::StringRef FunctionID, llvm::Module *Module,
}
static void fillMachineFunction(llvm::MachineFunction &MF,
+ llvm::ArrayRef<unsigned> LiveIns,
llvm::ArrayRef<llvm::MCInst> Instructions) {
llvm::MachineBasicBlock *MBB = MF.CreateMachineBasicBlock();
MF.push_back(MBB);
+ for (const unsigned Reg : LiveIns)
+ MBB->addLiveIn(Reg);
const llvm::MCInstrInfo *MCII = MF.getTarget().getMCInstrInfo();
llvm::DebugLoc DL;
for (const llvm::MCInst &Inst : Instructions) {
@@ -102,6 +111,8 @@ static void fillMachineFunction(llvm::MachineFunction &MF,
Builder.addReg(Op.getReg(), Flags);
} else if (Op.isImm()) {
Builder.addImm(Op.getImm());
+ } else if (!Op.isValid()) {
+ llvm_unreachable("Operand is not set");
} else {
llvm_unreachable("Not yet implemented");
}
@@ -114,7 +125,7 @@ static void fillMachineFunction(llvm::MachineFunction &MF,
} else {
llvm::MachineIRBuilder MIB(MF);
MIB.setMBB(*MBB);
- MF.getSubtarget().getCallLowering()->lowerReturn(MIB, nullptr, 0);
+ MF.getSubtarget().getCallLowering()->lowerReturn(MIB, nullptr, {});
}
}
@@ -131,17 +142,20 @@ llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM) {
llvm::make_unique<llvm::LLVMContext>();
std::unique_ptr<llvm::Module> Module =
createModule(Context, TM.createDataLayout());
+ // TODO: This only works for targets implementing LLVMTargetMachine.
+ const LLVMTargetMachine &LLVMTM = static_cast<const LLVMTargetMachine&>(TM);
std::unique_ptr<llvm::MachineModuleInfo> MMI =
- llvm::make_unique<llvm::MachineModuleInfo>(&TM);
+ llvm::make_unique<llvm::MachineModuleInfo>(&LLVMTM);
llvm::MachineFunction &MF =
- createVoidVoidMachineFunction(FunctionID, Module.get(), MMI.get());
+ createVoidVoidPtrMachineFunction(FunctionID, Module.get(), MMI.get());
// Saving reserved registers for client.
return MF.getSubtarget().getRegisterInfo()->getReservedRegs(MF);
}
void assembleToStream(const ExegesisTarget &ET,
std::unique_ptr<llvm::LLVMTargetMachine> TM,
- llvm::ArrayRef<unsigned> RegsToDef,
+ llvm::ArrayRef<unsigned> LiveIns,
+ llvm::ArrayRef<RegisterValue> RegisterInitialValues,
llvm::ArrayRef<llvm::MCInst> Instructions,
llvm::raw_pwrite_stream &AsmStream) {
std::unique_ptr<llvm::LLVMContext> Context =
@@ -151,21 +165,24 @@ void assembleToStream(const ExegesisTarget &ET,
std::unique_ptr<llvm::MachineModuleInfo> MMI =
llvm::make_unique<llvm::MachineModuleInfo>(TM.get());
llvm::MachineFunction &MF =
- createVoidVoidMachineFunction(FunctionID, Module.get(), MMI.get());
+ createVoidVoidPtrMachineFunction(FunctionID, Module.get(), MMI.get());
// We need to instruct the passes that we're done with SSA and virtual
// registers.
auto &Properties = MF.getProperties();
Properties.set(llvm::MachineFunctionProperties::Property::NoVRegs);
Properties.reset(llvm::MachineFunctionProperties::Property::IsSSA);
- bool IsSnippetSetupComplete = false;
- std::vector<llvm::MCInst> SnippetWithSetup =
- generateSnippetSetupCode(RegsToDef, ET, *TM, IsSnippetSetupComplete);
- if (!SnippetWithSetup.empty()) {
- SnippetWithSetup.insert(SnippetWithSetup.end(), Instructions.begin(),
- Instructions.end());
- Instructions = SnippetWithSetup;
- }
+
+ for (const unsigned Reg : LiveIns)
+ MF.getRegInfo().addLiveIn(Reg);
+
+ bool IsSnippetSetupComplete;
+ std::vector<llvm::MCInst> Code =
+ generateSnippetSetupCode(ET, TM->getMCSubtargetInfo(),
+ RegisterInitialValues, IsSnippetSetupComplete);
+
+ Code.insert(Code.end(), Instructions.begin(), Instructions.end());
+
// If the snippet setup is not complete, we disable liveliness tracking. This
// means that we won't know what values are in the registers.
if (!IsSnippetSetupComplete)
@@ -176,7 +193,7 @@ void assembleToStream(const ExegesisTarget &ET,
MF.getRegInfo().freezeReservedRegs(MF);
// Fill the MachineFunction from the instructions.
- fillMachineFunction(MF, Instructions);
+ fillMachineFunction(MF, LiveIns, Code);
// We create the pass manager, run the passes to populate AsmBuffer.
llvm::MCContext &MCContext = MMI->getContext();
@@ -281,3 +298,4 @@ ExecutableFunction::ExecutableFunction(
}
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/Assembler.h b/tools/llvm-exegesis/lib/Assembler.h
index e9a65cbbe369..ee6bc86f3788 100644
--- a/tools/llvm-exegesis/lib/Assembler.h
+++ b/tools/llvm-exegesis/lib/Assembler.h
@@ -18,6 +18,7 @@
#include <memory>
+#include "BenchmarkCode.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/CodeGen/MachineFunction.h"
@@ -31,6 +32,7 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
+namespace llvm {
namespace exegesis {
class ExegesisTarget;
@@ -39,13 +41,14 @@ class ExegesisTarget;
// convention and target machine).
llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM);
-// Creates a temporary `void foo()` function containing the provided
+// Creates a temporary `void foo(char*)` function containing the provided
// Instructions. Runs a set of llvm Passes to provide correct prologue and
// epilogue. Once the MachineFunction is ready, it is assembled for TM to
// AsmStream, the temporary function is eventually discarded.
void assembleToStream(const ExegesisTarget &ET,
std::unique_ptr<llvm::LLVMTargetMachine> TM,
- llvm::ArrayRef<unsigned> RegsToDef,
+ llvm::ArrayRef<unsigned> LiveIns,
+ llvm::ArrayRef<RegisterValue> RegisterInitialValues,
llvm::ArrayRef<llvm::MCInst> Instructions,
llvm::raw_pwrite_stream &AsmStream);
@@ -59,7 +62,7 @@ getObjectFromBuffer(llvm::StringRef Buffer);
llvm::object::OwningBinary<llvm::object::ObjectFile>
getObjectFromFile(llvm::StringRef Filename);
-// Consumes an ObjectFile containing a `void foo()` function and make it
+// Consumes an ObjectFile containing a `void foo(char*)` function and make it
// executable.
struct ExecutableFunction {
explicit ExecutableFunction(
@@ -70,7 +73,9 @@ struct ExecutableFunction {
llvm::StringRef getFunctionBytes() const { return FunctionBytes; }
// Executes the function.
- void operator()() const { ((void (*)())(intptr_t)FunctionBytes.data())(); }
+ void operator()(char *Memory) const {
+ ((void (*)(char *))(intptr_t)FunctionBytes.data())(Memory);
+ }
std::unique_ptr<llvm::LLVMContext> Context;
std::unique_ptr<llvm::ExecutionEngine> ExecEngine;
@@ -78,5 +83,6 @@ struct ExecutableFunction {
};
} // namespace exegesis
+} // namespace llvm
#endif // LLVM_TOOLS_LLVM_EXEGESIS_ASSEMBLER_H
diff --git a/tools/llvm-exegesis/lib/BenchmarkCode.h b/tools/llvm-exegesis/lib/BenchmarkCode.h
new file mode 100644
index 000000000000..38bea2519a64
--- /dev/null
+++ b/tools/llvm-exegesis/lib/BenchmarkCode.h
@@ -0,0 +1,41 @@
+//===-- BenchmarkCode.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKCODE_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKCODE_H
+
+#include "RegisterValue.h"
+#include "llvm/MC/MCInst.h"
+#include <string>
+#include <vector>
+
+namespace llvm {
+namespace exegesis {
+
+// A collection of instructions that are to be assembled, executed and measured.
+struct BenchmarkCode {
+ // The sequence of instructions that are to be repeated.
+ std::vector<llvm::MCInst> Instructions;
+
+ // Before the code is executed some instructions are added to setup the
+ // registers initial values.
+ std::vector<RegisterValue> RegisterInitialValues;
+
+ // We also need to provide the registers that are live on entry for the
+ // assembler to generate proper prologue/epilogue.
+ std::vector<unsigned> LiveIns;
+
+ // Informations about how this configuration was built.
+ std::string Info;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKCODE_H
diff --git a/tools/llvm-exegesis/lib/BenchmarkResult.cpp b/tools/llvm-exegesis/lib/BenchmarkResult.cpp
index 33ad65075d92..0507ae8959d0 100644
--- a/tools/llvm-exegesis/lib/BenchmarkResult.cpp
+++ b/tools/llvm-exegesis/lib/BenchmarkResult.cpp
@@ -8,7 +8,9 @@
//===----------------------------------------------------------------------===//
#include "BenchmarkResult.h"
+#include "BenchmarkRunner.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/bit.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ObjectYAML/YAML.h"
#include "llvm/Support/FileOutputBuffer.h"
@@ -16,82 +18,146 @@
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
-static constexpr const char kIntegerFormat[] = "i_0x%" PRId64 "x";
-static constexpr const char kDoubleFormat[] = "f_%la";
-
-static void serialize(const exegesis::BenchmarkResultContext &Context,
- const llvm::MCOperand &MCOperand, llvm::raw_ostream &OS) {
- if (MCOperand.isReg()) {
- OS << Context.getRegName(MCOperand.getReg());
- } else if (MCOperand.isImm()) {
- OS << llvm::format(kIntegerFormat, MCOperand.getImm());
- } else if (MCOperand.isFPImm()) {
- OS << llvm::format(kDoubleFormat, MCOperand.getFPImm());
- } else {
- OS << "INVALID";
+static constexpr const char kIntegerPrefix[] = "i_0x";
+static constexpr const char kDoublePrefix[] = "f_";
+static constexpr const char kInvalidOperand[] = "INVALID";
+
+namespace llvm {
+
+namespace {
+
+// A mutable struct holding an LLVMState that can be passed through the
+// serialization process to encode/decode registers and instructions.
+struct YamlContext {
+ YamlContext(const exegesis::LLVMState &State)
+ : State(&State), ErrorStream(LastError) {}
+
+ void serializeMCInst(const llvm::MCInst &MCInst, llvm::raw_ostream &OS) {
+ OS << getInstrName(MCInst.getOpcode());
+ for (const auto &Op : MCInst) {
+ OS << ' ';
+ serializeMCOperand(Op, OS);
+ }
}
-}
-static void serialize(const exegesis::BenchmarkResultContext &Context,
- const llvm::MCInst &MCInst, llvm::raw_ostream &OS) {
- OS << Context.getInstrName(MCInst.getOpcode());
- for (const auto &Op : MCInst) {
- OS << ' ';
- serialize(Context, Op, OS);
+ void deserializeMCInst(llvm::StringRef String, llvm::MCInst &Value) {
+ llvm::SmallVector<llvm::StringRef, 8> Pieces;
+ String.split(Pieces, " ", /* MaxSplit */ -1, /* KeepEmpty */ false);
+ if (Pieces.empty()) {
+ ErrorStream << "Unknown Instruction: '" << String << "'";
+ return;
+ }
+ bool ProcessOpcode = true;
+ for (llvm::StringRef Piece : Pieces) {
+ if (ProcessOpcode)
+ Value.setOpcode(getInstrOpcode(Piece));
+ else
+ Value.addOperand(deserializeMCOperand(Piece));
+ ProcessOpcode = false;
+ }
+ }
+
+ std::string &getLastError() { return ErrorStream.str(); }
+
+ llvm::raw_string_ostream &getErrorStream() { return ErrorStream; }
+
+ llvm::StringRef getRegName(unsigned RegNo) {
+ const llvm::StringRef RegName = State->getRegInfo().getName(RegNo);
+ if (RegName.empty())
+ ErrorStream << "No register with enum value" << RegNo;
+ return RegName;
}
-}
-static llvm::MCOperand
-deserialize(const exegesis::BenchmarkResultContext &Context,
- llvm::StringRef String) {
- assert(!String.empty());
- int64_t IntValue = 0;
- double DoubleValue = 0;
- if (sscanf(String.data(), kIntegerFormat, &IntValue) == 1)
- return llvm::MCOperand::createImm(IntValue);
- if (sscanf(String.data(), kDoubleFormat, &DoubleValue) == 1)
- return llvm::MCOperand::createFPImm(DoubleValue);
- if (unsigned RegNo = Context.getRegNo(String)) // Returns 0 if invalid.
- return llvm::MCOperand::createReg(RegNo);
- return {};
-}
+ unsigned getRegNo(llvm::StringRef RegName) {
+ const llvm::MCRegisterInfo &RegInfo = State->getRegInfo();
+ for (unsigned E = RegInfo.getNumRegs(), I = 0; I < E; ++I)
+ if (RegInfo.getName(I) == RegName)
+ return I;
+ ErrorStream << "No register with name " << RegName;
+ return 0;
+ }
-static llvm::StringRef
-deserialize(const exegesis::BenchmarkResultContext &Context,
- llvm::StringRef String, llvm::MCInst &Value) {
- llvm::SmallVector<llvm::StringRef, 8> Pieces;
- String.split(Pieces, " ");
- if (Pieces.empty())
- return "Invalid Instruction";
- bool ProcessOpcode = true;
- for (llvm::StringRef Piece : Pieces) {
- if (ProcessOpcode) {
- ProcessOpcode = false;
- Value.setOpcode(Context.getInstrOpcode(Piece));
- if (Value.getOpcode() == 0)
- return "Unknown Opcode Name";
+private:
+ void serializeIntegerOperand(llvm::raw_ostream &OS, int64_t Value) {
+ OS << kIntegerPrefix;
+ OS.write_hex(llvm::bit_cast<uint64_t>(Value));
+ }
+
+ bool tryDeserializeIntegerOperand(llvm::StringRef String, int64_t &Value) {
+ if (!String.consume_front(kIntegerPrefix))
+ return false;
+ return !String.consumeInteger(16, Value);
+ }
+
+ void serializeFPOperand(llvm::raw_ostream &OS, double Value) {
+ OS << kDoublePrefix << llvm::format("%la", Value);
+ }
+
+ bool tryDeserializeFPOperand(llvm::StringRef String, double &Value) {
+ if (!String.consume_front(kDoublePrefix))
+ return false;
+ char *EndPointer = nullptr;
+ Value = strtod(String.begin(), &EndPointer);
+ return EndPointer == String.end();
+ }
+
+ void serializeMCOperand(const llvm::MCOperand &MCOperand,
+ llvm::raw_ostream &OS) {
+ if (MCOperand.isReg()) {
+ OS << getRegName(MCOperand.getReg());
+ } else if (MCOperand.isImm()) {
+ serializeIntegerOperand(OS, MCOperand.getImm());
+ } else if (MCOperand.isFPImm()) {
+ serializeFPOperand(OS, MCOperand.getFPImm());
} else {
- Value.addOperand(deserialize(Context, Piece));
+ OS << kInvalidOperand;
}
}
- return {};
-}
-// YAML IO requires a mutable pointer to Context but we guarantee to not
-// modify it.
-static void *getUntypedContext(const exegesis::BenchmarkResultContext &Ctx) {
- return const_cast<exegesis::BenchmarkResultContext *>(&Ctx);
-}
+ llvm::MCOperand deserializeMCOperand(llvm::StringRef String) {
+ assert(!String.empty());
+ int64_t IntValue = 0;
+ double DoubleValue = 0;
+ if (tryDeserializeIntegerOperand(String, IntValue))
+ return llvm::MCOperand::createImm(IntValue);
+ if (tryDeserializeFPOperand(String, DoubleValue))
+ return llvm::MCOperand::createFPImm(DoubleValue);
+ if (unsigned RegNo = getRegNo(String))
+ return llvm::MCOperand::createReg(RegNo);
+ if (String != kInvalidOperand)
+ ErrorStream << "Unknown Operand: '" << String << "'";
+ return {};
+ }
-static const exegesis::BenchmarkResultContext &getTypedContext(void *Ctx) {
- assert(Ctx);
- return *static_cast<const exegesis::BenchmarkResultContext *>(Ctx);
-}
+ llvm::StringRef getInstrName(unsigned InstrNo) {
+ const llvm::StringRef InstrName = State->getInstrInfo().getName(InstrNo);
+ if (InstrName.empty())
+ ErrorStream << "No opcode with enum value" << InstrNo;
+ return InstrName;
+ }
+
+ unsigned getInstrOpcode(llvm::StringRef InstrName) {
+ const llvm::MCInstrInfo &InstrInfo = State->getInstrInfo();
+ for (unsigned E = InstrInfo.getNumOpcodes(), I = 0; I < E; ++I)
+ if (InstrInfo.getName(I) == InstrName)
+ return I;
+ ErrorStream << "No opcode with name " << InstrName;
+ return 0;
+ }
+
+ const llvm::exegesis::LLVMState *State;
+ std::string LastError;
+ llvm::raw_string_ostream ErrorStream;
+};
+} // namespace
// Defining YAML traits for IO.
-namespace llvm {
namespace yaml {
+static YamlContext &getTypedContext(void *Ctx) {
+ return *reinterpret_cast<YamlContext *>(Ctx);
+}
+
// std::vector<llvm::MCInst> will be rendered as a list.
template <> struct SequenceElementTraits<llvm::MCInst> {
static const bool flow = false;
@@ -101,13 +167,17 @@ template <> struct ScalarTraits<llvm::MCInst> {
static void output(const llvm::MCInst &Value, void *Ctx,
llvm::raw_ostream &Out) {
- serialize(getTypedContext(Ctx), Value, Out);
+ getTypedContext(Ctx).serializeMCInst(Value, Out);
}
static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) {
- return deserialize(getTypedContext(Ctx), Scalar, Value);
+ YamlContext &Context = getTypedContext(Ctx);
+ Context.deserializeMCInst(Scalar, Value);
+ return Context.getLastError();
}
+ // By default strings are quoted only when necessary.
+ // We force the use of single quotes for uniformity.
static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
static const bool flow = true;
@@ -123,8 +193,12 @@ template <> struct SequenceElementTraits<exegesis::BenchmarkMeasure> {
template <> struct MappingTraits<exegesis::BenchmarkMeasure> {
static void mapping(IO &Io, exegesis::BenchmarkMeasure &Obj) {
Io.mapRequired("key", Obj.Key);
- Io.mapRequired("value", Obj.Value);
- Io.mapOptional("debug_string", Obj.DebugString);
+ if (!Io.outputting()) {
+ // For backward compatibility, interpret debug_string as a key.
+ Io.mapOptional("debug_string", Obj.Key);
+ }
+ Io.mapRequired("value", Obj.PerInstructionValue);
+ Io.mapOptional("per_snippet_value", Obj.PerSnippetValue);
}
static const bool flow = true;
};
@@ -139,16 +213,58 @@ struct ScalarEnumerationTraits<exegesis::InstructionBenchmark::ModeE> {
}
};
-template <> struct MappingTraits<exegesis::InstructionBenchmarkKey> {
- static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj) {
+// std::vector<exegesis::RegisterValue> will be rendered as a list.
+template <> struct SequenceElementTraits<exegesis::RegisterValue> {
+ static const bool flow = false;
+};
+
+template <> struct ScalarTraits<exegesis::RegisterValue> {
+ static constexpr const unsigned kRadix = 16;
+ static constexpr const bool kSigned = false;
+
+ static void output(const exegesis::RegisterValue &RV, void *Ctx,
+ llvm::raw_ostream &Out) {
+ YamlContext &Context = getTypedContext(Ctx);
+ Out << Context.getRegName(RV.Register) << "=0x"
+ << RV.Value.toString(kRadix, kSigned);
+ }
+
+ static StringRef input(StringRef String, void *Ctx,
+ exegesis::RegisterValue &RV) {
+ llvm::SmallVector<llvm::StringRef, 2> Pieces;
+ String.split(Pieces, "=0x", /* MaxSplit */ -1,
+ /* KeepEmpty */ false);
+ YamlContext &Context = getTypedContext(Ctx);
+ if (Pieces.size() == 2) {
+ RV.Register = Context.getRegNo(Pieces[0]);
+ const unsigned BitsNeeded = llvm::APInt::getBitsNeeded(Pieces[1], kRadix);
+ RV.Value = llvm::APInt(BitsNeeded, Pieces[1], kRadix);
+ } else {
+ Context.getErrorStream()
+ << "Unknown initial register value: '" << String << "'";
+ }
+ return Context.getLastError();
+ }
+
+ static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
+
+ static const bool flow = true;
+};
+
+template <>
+struct MappingContextTraits<exegesis::InstructionBenchmarkKey, YamlContext> {
+ static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj,
+ YamlContext &Context) {
+ Io.setContext(&Context);
Io.mapRequired("instructions", Obj.Instructions);
Io.mapOptional("config", Obj.Config);
+ Io.mapRequired("register_initial_values", Obj.RegisterInitialValues);
}
};
-template <> struct MappingTraits<exegesis::InstructionBenchmark> {
- class NormalizedBinary {
- public:
+template <>
+struct MappingContextTraits<exegesis::InstructionBenchmark, YamlContext> {
+ struct NormalizedBinary {
NormalizedBinary(IO &io) {}
NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {}
std::vector<uint8_t> denormalize(IO &) {
@@ -164,9 +280,10 @@ template <> struct MappingTraits<exegesis::InstructionBenchmark> {
BinaryRef Binary;
};
- static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj) {
+ static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj,
+ YamlContext &Context) {
Io.mapRequired("mode", Obj.Mode);
- Io.mapRequired("key", Obj.Key);
+ Io.mapRequired("key", Obj.Key, Context);
Io.mapRequired("cpu_name", Obj.CpuName);
Io.mapRequired("llvm_triple", Obj.LLVMTriple);
Io.mapRequired("num_repetitions", Obj.NumRepetitions);
@@ -181,101 +298,71 @@ template <> struct MappingTraits<exegesis::InstructionBenchmark> {
};
} // namespace yaml
-} // namespace llvm
-
-LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(exegesis::InstructionBenchmark)
namespace exegesis {
-void BenchmarkResultContext::addRegEntry(unsigned RegNo, llvm::StringRef Name) {
- assert(RegNoToName.find(RegNo) == RegNoToName.end());
- assert(RegNameToNo.find(Name) == RegNameToNo.end());
- RegNoToName[RegNo] = Name;
- RegNameToNo[Name] = RegNo;
-}
-
-llvm::StringRef BenchmarkResultContext::getRegName(unsigned RegNo) const {
- const auto Itr = RegNoToName.find(RegNo);
- if (Itr != RegNoToName.end())
- return Itr->second;
- return {};
-}
-
-unsigned BenchmarkResultContext::getRegNo(llvm::StringRef Name) const {
- const auto Itr = RegNameToNo.find(Name);
- if (Itr != RegNameToNo.end())
- return Itr->second;
- return 0;
-}
-
-void BenchmarkResultContext::addInstrEntry(unsigned Opcode,
- llvm::StringRef Name) {
- assert(InstrOpcodeToName.find(Opcode) == InstrOpcodeToName.end());
- assert(InstrNameToOpcode.find(Name) == InstrNameToOpcode.end());
- InstrOpcodeToName[Opcode] = Name;
- InstrNameToOpcode[Name] = Opcode;
-}
-
-llvm::StringRef BenchmarkResultContext::getInstrName(unsigned Opcode) const {
- const auto Itr = InstrOpcodeToName.find(Opcode);
- if (Itr != InstrOpcodeToName.end())
- return Itr->second;
- return {};
-}
-
-unsigned BenchmarkResultContext::getInstrOpcode(llvm::StringRef Name) const {
- const auto Itr = InstrNameToOpcode.find(Name);
- if (Itr != InstrNameToOpcode.end())
- return Itr->second;
- return 0;
-}
-
-template <typename ObjectOrList>
-static llvm::Expected<ObjectOrList>
-readYamlCommon(const BenchmarkResultContext &Context,
- llvm::StringRef Filename) {
+llvm::Expected<InstructionBenchmark>
+InstructionBenchmark::readYaml(const LLVMState &State,
+ llvm::StringRef Filename) {
if (auto ExpectedMemoryBuffer =
llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) {
- std::unique_ptr<llvm::MemoryBuffer> MemoryBuffer =
- std::move(ExpectedMemoryBuffer.get());
- llvm::yaml::Input Yin(*MemoryBuffer, getUntypedContext(Context));
- ObjectOrList Benchmark;
- Yin >> Benchmark;
+ llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get());
+ YamlContext Context(State);
+ InstructionBenchmark Benchmark;
+ if (Yin.setCurrentDocument())
+ llvm::yaml::yamlize(Yin, Benchmark, /*unused*/ true, Context);
+ if (!Context.getLastError().empty())
+ return llvm::make_error<BenchmarkFailure>(Context.getLastError());
return Benchmark;
} else {
return ExpectedMemoryBuffer.takeError();
}
}
-llvm::Expected<InstructionBenchmark>
-InstructionBenchmark::readYaml(const BenchmarkResultContext &Context,
- llvm::StringRef Filename) {
- return readYamlCommon<InstructionBenchmark>(Context, Filename);
-}
-
llvm::Expected<std::vector<InstructionBenchmark>>
-InstructionBenchmark::readYamls(const BenchmarkResultContext &Context,
+InstructionBenchmark::readYamls(const LLVMState &State,
llvm::StringRef Filename) {
- return readYamlCommon<std::vector<InstructionBenchmark>>(Context, Filename);
+ if (auto ExpectedMemoryBuffer =
+ llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) {
+ llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get());
+ YamlContext Context(State);
+ std::vector<InstructionBenchmark> Benchmarks;
+ while (Yin.setCurrentDocument()) {
+ Benchmarks.emplace_back();
+ yamlize(Yin, Benchmarks.back(), /*unused*/ true, Context);
+ if (Yin.error())
+ return llvm::errorCodeToError(Yin.error());
+ if (!Context.getLastError().empty())
+ return llvm::make_error<BenchmarkFailure>(Context.getLastError());
+ Yin.nextDocument();
+ }
+ return Benchmarks;
+ } else {
+ return ExpectedMemoryBuffer.takeError();
+ }
}
-void InstructionBenchmark::writeYamlTo(const BenchmarkResultContext &Context,
+void InstructionBenchmark::writeYamlTo(const LLVMState &State,
llvm::raw_ostream &OS) {
- llvm::yaml::Output Yout(OS, getUntypedContext(Context));
- Yout << *this;
+ llvm::yaml::Output Yout(OS, nullptr /*Ctx*/, 200 /*WrapColumn*/);
+ YamlContext Context(State);
+ Yout.beginDocuments();
+ llvm::yaml::yamlize(Yout, *this, /*unused*/ true, Context);
+ Yout.endDocuments();
}
-void InstructionBenchmark::readYamlFrom(const BenchmarkResultContext &Context,
+void InstructionBenchmark::readYamlFrom(const LLVMState &State,
llvm::StringRef InputContent) {
- llvm::yaml::Input Yin(InputContent, getUntypedContext(Context));
- Yin >> *this;
+ llvm::yaml::Input Yin(InputContent);
+ YamlContext Context(State);
+ if (Yin.setCurrentDocument())
+ llvm::yaml::yamlize(Yin, *this, /*unused*/ true, Context);
}
-llvm::Error
-InstructionBenchmark::writeYaml(const BenchmarkResultContext &Context,
- const llvm::StringRef Filename) {
+llvm::Error InstructionBenchmark::writeYaml(const LLVMState &State,
+ const llvm::StringRef Filename) {
if (Filename == "-") {
- writeYamlTo(Context, llvm::outs());
+ writeYamlTo(State, llvm::outs());
} else {
int ResultFD = 0;
if (auto E = llvm::errorCodeToError(
@@ -284,19 +371,20 @@ InstructionBenchmark::writeYaml(const BenchmarkResultContext &Context,
return E;
}
llvm::raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/);
- writeYamlTo(Context, Ostr);
+ writeYamlTo(State, Ostr);
}
return llvm::Error::success();
}
-void BenchmarkMeasureStats::push(const BenchmarkMeasure &BM) {
+void PerInstructionStats::push(const BenchmarkMeasure &BM) {
if (Key.empty())
Key = BM.Key;
assert(Key == BM.Key);
++NumValues;
- SumValues += BM.Value;
- MaxValue = std::max(MaxValue, BM.Value);
- MinValue = std::min(MinValue, BM.Value);
+ SumValues += BM.PerInstructionValue;
+ MaxValue = std::max(MaxValue, BM.PerInstructionValue);
+ MinValue = std::min(MinValue, BM.PerInstructionValue);
}
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/BenchmarkResult.h b/tools/llvm-exegesis/lib/BenchmarkResult.h
index c9e77ca49223..773a2e50abc4 100644
--- a/tools/llvm-exegesis/lib/BenchmarkResult.h
+++ b/tools/llvm-exegesis/lib/BenchmarkResult.h
@@ -16,6 +16,8 @@
#ifndef LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H
#define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H
+#include "BenchmarkCode.h"
+#include "LlvmState.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCInst.h"
@@ -26,22 +28,31 @@
#include <unordered_map>
#include <vector>
+namespace llvm {
namespace exegesis {
-struct BenchmarkResultContext; // Forward declaration.
-
struct InstructionBenchmarkKey {
// The LLVM opcode name.
std::vector<llvm::MCInst> Instructions;
+ // The initial values of the registers.
+ std::vector<RegisterValue> RegisterInitialValues;
// An opaque configuration, that can be used to separate several benchmarks of
// the same instruction under different configurations.
std::string Config;
};
struct BenchmarkMeasure {
+ // A helper to create an unscaled BenchmarkMeasure.
+ static BenchmarkMeasure Create(std::string Key, double Value) {
+ return {Key, Value, Value};
+ }
std::string Key;
- double Value;
- std::string DebugString;
+ // This is the per-instruction value, i.e. measured quantity scaled per
+ // instruction.
+ double PerInstructionValue;
+ // This is the per-snippet value, i.e. measured quantity for one repetition of
+ // the whole snippet.
+ double PerSnippetValue;
};
// The result of an instruction benchmark.
@@ -62,26 +73,24 @@ struct InstructionBenchmark {
// Read functions.
static llvm::Expected<InstructionBenchmark>
- readYaml(const BenchmarkResultContext &Context, llvm::StringRef Filename);
+ readYaml(const LLVMState &State, llvm::StringRef Filename);
static llvm::Expected<std::vector<InstructionBenchmark>>
- readYamls(const BenchmarkResultContext &Context, llvm::StringRef Filename);
+ readYamls(const LLVMState &State, llvm::StringRef Filename);
- void readYamlFrom(const BenchmarkResultContext &Context,
- llvm::StringRef InputContent);
+ void readYamlFrom(const LLVMState &State, llvm::StringRef InputContent);
// Write functions, non-const because of YAML traits.
- void writeYamlTo(const BenchmarkResultContext &Context, llvm::raw_ostream &S);
+ void writeYamlTo(const LLVMState &State, llvm::raw_ostream &S);
- llvm::Error writeYaml(const BenchmarkResultContext &Context,
- const llvm::StringRef Filename);
+ llvm::Error writeYaml(const LLVMState &State, const llvm::StringRef Filename);
};
//------------------------------------------------------------------------------
// Utilities to work with Benchmark measures.
// A class that measures stats over benchmark measures.
-class BenchmarkMeasureStats {
+class PerInstructionStats {
public:
void push(const BenchmarkMeasure &BM);
@@ -102,38 +111,7 @@ private:
double MinValue = std::numeric_limits<double>::max();
};
-// This context is used when de/serializing InstructionBenchmark to guarantee
-// that Registers and Instructions are human readable and preserved accross
-// different versions of LLVM.
-struct BenchmarkResultContext {
- BenchmarkResultContext() = default;
- BenchmarkResultContext(BenchmarkResultContext &&) = default;
- BenchmarkResultContext &operator=(BenchmarkResultContext &&) = default;
- BenchmarkResultContext(const BenchmarkResultContext &) = delete;
- BenchmarkResultContext &operator=(const BenchmarkResultContext &) = delete;
-
- // Populate Registers and Instruction mapping.
- void addRegEntry(unsigned RegNo, llvm::StringRef Name);
- void addInstrEntry(unsigned Opcode, llvm::StringRef Name);
-
- // Register accessors.
- llvm::StringRef getRegName(unsigned RegNo) const;
- unsigned getRegNo(llvm::StringRef Name) const; // 0 is not found.
-
- // Instruction accessors.
- llvm::StringRef getInstrName(unsigned Opcode) const;
- unsigned getInstrOpcode(llvm::StringRef Name) const; // 0 is not found.
-
-private:
- // Ideally we would like to use MCRegisterInfo and MCInstrInfo but doing so
- // would make testing harder, instead we create a mapping that we can easily
- // populate.
- std::unordered_map<unsigned, llvm::StringRef> InstrOpcodeToName;
- std::unordered_map<unsigned, llvm::StringRef> RegNoToName;
- llvm::StringMap<unsigned> InstrNameToOpcode;
- llvm::StringMap<unsigned> RegNameToNo;
-};
-
} // namespace exegesis
+} // namespace llvm
#endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H
diff --git a/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
index 55012bc1e83f..437503f84865 100644
--- a/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
+++ b/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
@@ -13,14 +13,16 @@
#include "Assembler.h"
#include "BenchmarkRunner.h"
#include "MCInstrDescView.h"
+#include "PerfHelper.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Program.h"
+namespace llvm {
namespace exegesis {
BenchmarkFailure::BenchmarkFailure(const llvm::Twine &S)
@@ -28,74 +30,94 @@ BenchmarkFailure::BenchmarkFailure(const llvm::Twine &S)
BenchmarkRunner::BenchmarkRunner(const LLVMState &State,
InstructionBenchmark::ModeE Mode)
- : State(State), RATC(State.getRegInfo(),
- getFunctionReservedRegs(State.getTargetMachine())),
- Mode(Mode) {}
+ : State(State), Mode(Mode), Scratch(llvm::make_unique<ScratchSpace>()) {}
BenchmarkRunner::~BenchmarkRunner() = default;
-llvm::Expected<std::vector<InstructionBenchmark>>
-BenchmarkRunner::run(unsigned Opcode, unsigned NumRepetitions) {
- const llvm::MCInstrDesc &InstrDesc = State.getInstrInfo().get(Opcode);
- // Ignore instructions that we cannot run.
- if (InstrDesc.isPseudo())
- return llvm::make_error<BenchmarkFailure>("Unsupported opcode: isPseudo");
- if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch())
- return llvm::make_error<BenchmarkFailure>(
- "Unsupported opcode: isBranch/isIndirectBranch");
- if (InstrDesc.isCall() || InstrDesc.isReturn())
- return llvm::make_error<BenchmarkFailure>(
- "Unsupported opcode: isCall/isReturn");
-
- llvm::Expected<std::vector<BenchmarkConfiguration>> ConfigurationOrError =
- generateConfigurations(Opcode);
-
- if (llvm::Error E = ConfigurationOrError.takeError())
- return std::move(E);
-
- std::vector<InstructionBenchmark> InstrBenchmarks;
- for (const BenchmarkConfiguration &Conf : ConfigurationOrError.get())
- InstrBenchmarks.push_back(runOne(Conf, Opcode, NumRepetitions));
- return InstrBenchmarks;
+// Repeat the snippet until there are at least MinInstructions in the resulting
+// code.
+static std::vector<llvm::MCInst>
+GenerateInstructions(const BenchmarkCode &BC, const size_t MinInstructions) {
+ if (BC.Instructions.empty())
+ return {};
+ std::vector<llvm::MCInst> Code = BC.Instructions;
+ for (int I = 0; Code.size() < MinInstructions; ++I)
+ Code.push_back(BC.Instructions[I % BC.Instructions.size()]);
+ return Code;
}
+namespace {
+class FunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
+public:
+ FunctionExecutorImpl(const LLVMState &State,
+ llvm::object::OwningBinary<llvm::object::ObjectFile> Obj,
+ BenchmarkRunner::ScratchSpace *Scratch)
+ : Function(State.createTargetMachine(), std::move(Obj)),
+ Scratch(Scratch) {}
+
+private:
+ llvm::Expected<int64_t> runAndMeasure(const char *Counters) const override {
+ // We sum counts when there are several counters for a single ProcRes
+ // (e.g. P23 on SandyBridge).
+ int64_t CounterValue = 0;
+ llvm::SmallVector<llvm::StringRef, 2> CounterNames;
+ llvm::StringRef(Counters).split(CounterNames, '+');
+ char *const ScratchPtr = Scratch->ptr();
+ for (auto &CounterName : CounterNames) {
+ CounterName = CounterName.trim();
+ pfm::PerfEvent PerfEvent(CounterName);
+ if (!PerfEvent.valid())
+ llvm::report_fatal_error(
+ llvm::Twine("invalid perf event '").concat(CounterName).concat("'"));
+ pfm::Counter Counter(PerfEvent);
+ Scratch->clear();
+ {
+ llvm::CrashRecoveryContext CRC;
+ llvm::CrashRecoveryContext::Enable();
+ const bool Crashed = !CRC.RunSafely([this, &Counter, ScratchPtr]() {
+ Counter.start();
+ this->Function(ScratchPtr);
+ Counter.stop();
+ });
+ llvm::CrashRecoveryContext::Disable();
+ // FIXME: Better diagnosis.
+ if (Crashed)
+ return llvm::make_error<BenchmarkFailure>(
+ "snippet crashed while running");
+ }
+ CounterValue += Counter.read();
+ }
+ return CounterValue;
+ }
+
+ const ExecutableFunction Function;
+ BenchmarkRunner::ScratchSpace *const Scratch;
+};
+} // namespace
+
InstructionBenchmark
-BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration,
- unsigned Opcode, unsigned NumRepetitions) const {
+BenchmarkRunner::runConfiguration(const BenchmarkCode &BC,
+ unsigned NumRepetitions) const {
InstructionBenchmark InstrBenchmark;
InstrBenchmark.Mode = Mode;
InstrBenchmark.CpuName = State.getTargetMachine().getTargetCPU();
InstrBenchmark.LLVMTriple =
State.getTargetMachine().getTargetTriple().normalize();
InstrBenchmark.NumRepetitions = NumRepetitions;
- InstrBenchmark.Info = Configuration.Info;
+ InstrBenchmark.Info = BC.Info;
- const std::vector<llvm::MCInst> &Snippet = Configuration.Snippet;
- if (Snippet.empty()) {
- InstrBenchmark.Error = "Empty snippet";
- return InstrBenchmark;
- }
-
- InstrBenchmark.Key.Instructions = Snippet;
+ const std::vector<llvm::MCInst> &Instructions = BC.Instructions;
- // Repeat the snippet until there are at least NumInstructions in the
- // resulting code. The snippet is always repeated at least once.
- const auto GenerateInstructions = [&Configuration](
- const int MinInstructions) {
- std::vector<llvm::MCInst> Code = Configuration.Snippet;
- for (int I = 0; I < MinInstructions; ++I)
- Code.push_back(Configuration.Snippet[I % Configuration.Snippet.size()]);
- return Code;
- };
+ InstrBenchmark.Key.Instructions = Instructions;
+ InstrBenchmark.Key.RegisterInitialValues = BC.RegisterInitialValues;
// Assemble at least kMinInstructionsForSnippet instructions by repeating the
// snippet for debug/analysis. This is so that the user clearly understands
// that the inside instructions are repeated.
constexpr const int kMinInstructionsForSnippet = 16;
{
- auto ObjectFilePath =
- writeObjectFile(Configuration.SnippetSetup,
- GenerateInstructions(kMinInstructionsForSnippet));
+ auto ObjectFilePath = writeObjectFile(
+ BC, GenerateInstructions(BC, kMinInstructionsForSnippet));
if (llvm::Error E = ObjectFilePath.takeError()) {
InstrBenchmark.Error = llvm::toString(std::move(E));
return InstrBenchmark;
@@ -108,82 +130,36 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration,
// Assemble NumRepetitions instructions repetitions of the snippet for
// measurements.
- auto ObjectFilePath =
- writeObjectFile(Configuration.SnippetSetup,
- GenerateInstructions(InstrBenchmark.NumRepetitions));
+ auto ObjectFilePath = writeObjectFile(
+ BC, GenerateInstructions(BC, InstrBenchmark.NumRepetitions));
if (llvm::Error E = ObjectFilePath.takeError()) {
InstrBenchmark.Error = llvm::toString(std::move(E));
return InstrBenchmark;
}
llvm::outs() << "Check generated assembly with: /usr/bin/objdump -d "
<< *ObjectFilePath << "\n";
- const ExecutableFunction EF(State.createTargetMachine(),
- getObjectFromFile(*ObjectFilePath));
- InstrBenchmark.Measurements = runMeasurements(EF, NumRepetitions);
+ const FunctionExecutorImpl Executor(State, getObjectFromFile(*ObjectFilePath),
+ Scratch.get());
+ auto Measurements = runMeasurements(Executor);
+ if (llvm::Error E = Measurements.takeError()) {
+ InstrBenchmark.Error = llvm::toString(std::move(E));
+ return InstrBenchmark;
+ }
+ InstrBenchmark.Measurements = std::move(*Measurements);
+ assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions");
+ for (BenchmarkMeasure &BM : InstrBenchmark.Measurements) {
+ // Scale the measurements by instruction.
+ BM.PerInstructionValue /= InstrBenchmark.NumRepetitions;
+ // Scale the measurements by snippet.
+ BM.PerSnippetValue *= static_cast<double>(BC.Instructions.size()) /
+ InstrBenchmark.NumRepetitions;
+ }
return InstrBenchmark;
}
-llvm::Expected<std::vector<BenchmarkConfiguration>>
-BenchmarkRunner::generateConfigurations(unsigned Opcode) const {
- if (auto E = generatePrototype(Opcode)) {
- SnippetPrototype &Prototype = E.get();
- // TODO: Generate as many configurations as needed here.
- BenchmarkConfiguration Configuration;
- Configuration.Info = Prototype.Explanation;
- for (InstructionInstance &II : Prototype.Snippet) {
- II.randomizeUnsetVariables();
- Configuration.Snippet.push_back(II.build());
- }
- Configuration.SnippetSetup.RegsToDef = computeRegsToDef(Prototype.Snippet);
- return std::vector<BenchmarkConfiguration>{Configuration};
- } else
- return E.takeError();
-}
-
-std::vector<unsigned> BenchmarkRunner::computeRegsToDef(
- const std::vector<InstructionInstance> &Snippet) const {
- // Collect all register uses and create an assignment for each of them.
- // Loop invariant: DefinedRegs[i] is true iif it has been set at least once
- // before the current instruction.
- llvm::BitVector DefinedRegs = RATC.emptyRegisters();
- std::vector<unsigned> RegsToDef;
- for (const InstructionInstance &II : Snippet) {
- // Returns the register that this Operand sets or uses, or 0 if this is not
- // a register.
- const auto GetOpReg = [&II](const Operand &Op) -> unsigned {
- if (Op.ImplicitReg) {
- return *Op.ImplicitReg;
- } else if (Op.IsExplicit && II.getValueFor(Op).isReg()) {
- return II.getValueFor(Op).getReg();
- }
- return 0;
- };
- // Collect used registers that have never been def'ed.
- for (const Operand &Op : II.Instr.Operands) {
- if (!Op.IsDef) {
- const unsigned Reg = GetOpReg(Op);
- if (Reg > 0 && !DefinedRegs.test(Reg)) {
- RegsToDef.push_back(Reg);
- DefinedRegs.set(Reg);
- }
- }
- }
- // Mark defs as having been def'ed.
- for (const Operand &Op : II.Instr.Operands) {
- if (Op.IsDef) {
- const unsigned Reg = GetOpReg(Op);
- if (Reg > 0) {
- DefinedRegs.set(Reg);
- }
- }
- }
- }
- return RegsToDef;
-}
-
llvm::Expected<std::string>
-BenchmarkRunner::writeObjectFile(const BenchmarkConfiguration::Setup &Setup,
+BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC,
llvm::ArrayRef<llvm::MCInst> Code) const {
int ResultFD = 0;
llvm::SmallString<256> ResultPath;
@@ -192,38 +168,11 @@ BenchmarkRunner::writeObjectFile(const BenchmarkConfiguration::Setup &Setup,
return std::move(E);
llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/);
assembleToStream(State.getExegesisTarget(), State.createTargetMachine(),
- Setup.RegsToDef, Code, OFS);
+ BC.LiveIns, BC.RegisterInitialValues, Code, OFS);
return ResultPath.str();
}
-llvm::Expected<SnippetPrototype>
-BenchmarkRunner::generateSelfAliasingPrototype(const Instruction &Instr) const {
- const AliasingConfigurations SelfAliasing(Instr, Instr);
- if (SelfAliasing.empty()) {
- return llvm::make_error<BenchmarkFailure>("empty self aliasing");
- }
- SnippetPrototype Prototype;
- InstructionInstance II(Instr);
- if (SelfAliasing.hasImplicitAliasing()) {
- Prototype.Explanation = "implicit Self cycles, picking random values.";
- } else {
- Prototype.Explanation =
- "explicit self cycles, selecting one aliasing Conf.";
- // This is a self aliasing instruction so defs and uses are from the same
- // instance, hence twice II in the following call.
- setRandomAliasing(SelfAliasing, II, II);
- }
- Prototype.Snippet.push_back(std::move(II));
- return std::move(Prototype);
-}
+BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {}
-llvm::Expected<SnippetPrototype>
-BenchmarkRunner::generateUnconstrainedPrototype(const Instruction &Instr,
- llvm::StringRef Msg) const {
- SnippetPrototype Prototype;
- Prototype.Explanation =
- llvm::formatv("{0}, repeating an unconstrained assignment", Msg);
- Prototype.Snippet.emplace_back(Instr);
- return std::move(Prototype);
-}
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/BenchmarkRunner.h b/tools/llvm-exegesis/lib/BenchmarkRunner.h
index 91d2c8e2d8c0..4f77f492ab4b 100644
--- a/tools/llvm-exegesis/lib/BenchmarkRunner.h
+++ b/tools/llvm-exegesis/lib/BenchmarkRunner.h
@@ -17,14 +17,17 @@
#define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H
#include "Assembler.h"
+#include "BenchmarkCode.h"
#include "BenchmarkResult.h"
#include "LlvmState.h"
#include "MCInstrDescView.h"
-#include "RegisterAliasing.h"
#include "llvm/MC/MCInst.h"
#include "llvm/Support/Error.h"
+#include <cstdlib>
+#include <memory>
#include <vector>
+namespace llvm {
namespace exegesis {
// A class representing failures that happened during Benchmark, they are used
@@ -34,23 +37,6 @@ public:
BenchmarkFailure(const llvm::Twine &S);
};
-// A collection of instructions that are to be assembled, executed and measured.
-struct BenchmarkConfiguration {
- // This code is run before the Snippet is iterated. Since it is part of the
- // measurement it should be as short as possible. It is usually used to setup
- // the content of the Registers.
- struct Setup {
- std::vector<unsigned> RegsToDef;
- };
- Setup SnippetSetup;
-
- // The sequence of instructions that are to be repeated.
- std::vector<llvm::MCInst> Snippet;
-
- // Informations about how this configuration was built.
- std::string Info;
-};
-
// Common code for all benchmark modes.
class BenchmarkRunner {
public:
@@ -59,50 +45,52 @@ public:
virtual ~BenchmarkRunner();
- llvm::Expected<std::vector<InstructionBenchmark>>
- run(unsigned Opcode, unsigned NumRepetitions);
+ InstructionBenchmark runConfiguration(const BenchmarkCode &Configuration,
+ unsigned NumRepetitions) const;
+
+ // Scratch space to run instructions that touch memory.
+ struct ScratchSpace {
+ static constexpr const size_t kAlignment = 1024;
+ static constexpr const size_t kSize = 1 << 20; // 1MB.
+ ScratchSpace()
+ : UnalignedPtr(llvm::make_unique<char[]>(kSize + kAlignment)),
+ AlignedPtr(
+ UnalignedPtr.get() + kAlignment -
+ (reinterpret_cast<intptr_t>(UnalignedPtr.get()) % kAlignment)) {}
+ char *ptr() const { return AlignedPtr; }
+ void clear() { std::memset(ptr(), 0, kSize); }
+
+ private:
+ const std::unique_ptr<char[]> UnalignedPtr;
+ char *const AlignedPtr;
+ };
- // Given a snippet, computes which registers the setup code needs to define.
- std::vector<unsigned>
- computeRegsToDef(const std::vector<InstructionInstance> &Snippet) const;
+ // A helper to measure counters while executing a function in a sandboxed
+ // context.
+ class FunctionExecutor {
+ public:
+ virtual ~FunctionExecutor();
+ virtual llvm::Expected<int64_t>
+ runAndMeasure(const char *Counters) const = 0;
+ };
protected:
const LLVMState &State;
- const RegisterAliasingTrackerCache RATC;
-
- // Generates a single instruction prototype that has a self-dependency.
- llvm::Expected<SnippetPrototype>
- generateSelfAliasingPrototype(const Instruction &Instr) const;
- // Generates a single instruction prototype without assignment constraints.
- llvm::Expected<SnippetPrototype>
- generateUnconstrainedPrototype(const Instruction &Instr,
- llvm::StringRef Msg) const;
private:
- // API to be implemented by subclasses.
- virtual llvm::Expected<SnippetPrototype>
- generatePrototype(unsigned Opcode) const = 0;
-
- virtual std::vector<BenchmarkMeasure>
- runMeasurements(const ExecutableFunction &EF,
- const unsigned NumRepetitions) const = 0;
-
- // Internal helpers.
- InstructionBenchmark runOne(const BenchmarkConfiguration &Configuration,
- unsigned Opcode, unsigned NumRepetitions) const;
-
- // Calls generatePrototype and expands the SnippetPrototype into one or more
- // BenchmarkConfiguration.
- llvm::Expected<std::vector<BenchmarkConfiguration>>
- generateConfigurations(unsigned Opcode) const;
+ virtual llvm::Expected<std::vector<BenchmarkMeasure>>
+ runMeasurements(const FunctionExecutor &Executor) const = 0;
llvm::Expected<std::string>
- writeObjectFile(const BenchmarkConfiguration::Setup &Setup,
+ writeObjectFile(const BenchmarkCode &Configuration,
llvm::ArrayRef<llvm::MCInst> Code) const;
const InstructionBenchmark::ModeE Mode;
+
+ const std::unique_ptr<ScratchSpace> Scratch;
};
} // namespace exegesis
+} // namespace llvm
#endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H
diff --git a/tools/llvm-exegesis/lib/CMakeLists.txt b/tools/llvm-exegesis/lib/CMakeLists.txt
index 175c2adf9de8..ef85056db0a4 100644
--- a/tools/llvm-exegesis/lib/CMakeLists.txt
+++ b/tools/llvm-exegesis/lib/CMakeLists.txt
@@ -1,12 +1,20 @@
+set(TARGETS_TO_APPEND "")
+
if (LLVM_TARGETS_TO_BUILD MATCHES "X86")
add_subdirectory(X86)
- set(LLVM_EXEGESIS_TARGETS "${LLVM_EXEGESIS_TARGETS} X86" PARENT_SCOPE)
+ set(TARGETS_TO_APPEND "${TARGETS_TO_APPEND} X86")
endif()
if (LLVM_TARGETS_TO_BUILD MATCHES "AArch64")
add_subdirectory(AArch64)
- set(LLVM_EXEGESIS_TARGETS "${LLVM_EXEGESIS_TARGETS} AArch64" PARENT_SCOPE)
+ set(TARGETS_TO_APPEND "${TARGETS_TO_APPEND} AArch64")
+endif()
+if (LLVM_TARGETS_TO_BUILD MATCHES "PowerPC")
+ add_subdirectory(PowerPC)
+ set(TARGETS_TO_APPEND "${TARGETS_TO_APPEND} PowerPC")
endif()
+set(LLVM_EXEGESIS_TARGETS "${LLVM_EXEGESIS_TARGETS} ${TARGETS_TO_APPEND}" PARENT_SCOPE)
+
add_library(LLVMExegesis
STATIC
Analysis.cpp
@@ -14,11 +22,14 @@ add_library(LLVMExegesis
BenchmarkResult.cpp
BenchmarkRunner.cpp
Clustering.cpp
+ CodeTemplate.cpp
Latency.cpp
LlvmState.cpp
MCInstrDescView.cpp
PerfHelper.cpp
RegisterAliasing.cpp
+ SnippetGenerator.cpp
+ RegisterValue.cpp
Target.cpp
Uops.cpp
)
diff --git a/tools/llvm-exegesis/lib/Clustering.cpp b/tools/llvm-exegesis/lib/Clustering.cpp
index a966c722a78f..b2cd97c12eb0 100644
--- a/tools/llvm-exegesis/lib/Clustering.cpp
+++ b/tools/llvm-exegesis/lib/Clustering.cpp
@@ -8,9 +8,11 @@
//===----------------------------------------------------------------------===//
#include "Clustering.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallVector.h"
#include <string>
-#include <unordered_set>
+namespace llvm {
namespace exegesis {
// The clustering problem has the following characteristics:
@@ -31,9 +33,10 @@ namespace exegesis {
// Finds the points at distance less than sqrt(EpsilonSquared) of Q (not
// including Q).
-std::vector<size_t>
-InstructionBenchmarkClustering::rangeQuery(const size_t Q) const {
- std::vector<size_t> Neighbors;
+void InstructionBenchmarkClustering::rangeQuery(
+ const size_t Q, std::vector<size_t> &Neighbors) const {
+ Neighbors.clear();
+ Neighbors.reserve(Points_.size() - 1); // The Q itself isn't a neighbor.
const auto &QMeasurements = Points_[Q].Measurements;
for (size_t P = 0, NumPoints = Points_.size(); P < NumPoints; ++P) {
if (P == Q)
@@ -45,18 +48,6 @@ InstructionBenchmarkClustering::rangeQuery(const size_t Q) const {
Neighbors.push_back(P);
}
}
- return Neighbors;
-}
-
-bool InstructionBenchmarkClustering::isNeighbour(
- const std::vector<BenchmarkMeasure> &P,
- const std::vector<BenchmarkMeasure> &Q) const {
- double DistanceSquared = 0.0;
- for (size_t I = 0, E = P.size(); I < E; ++I) {
- const auto Diff = P[I].Value - Q[I].Value;
- DistanceSquared += Diff * Diff;
- }
- return DistanceSquared <= EpsilonSquared_;
}
InstructionBenchmarkClustering::InstructionBenchmarkClustering(
@@ -101,10 +92,11 @@ llvm::Error InstructionBenchmarkClustering::validateAndSetup() {
}
void InstructionBenchmarkClustering::dbScan(const size_t MinPts) {
+ std::vector<size_t> Neighbors; // Persistent buffer to avoid allocs.
for (size_t P = 0, NumPoints = Points_.size(); P < NumPoints; ++P) {
if (!ClusterIdForPoint_[P].isUndef())
continue; // Previously processed in inner loop.
- const auto Neighbors = rangeQuery(P);
+ rangeQuery(P, Neighbors);
if (Neighbors.size() + 1 < MinPts) { // Density check.
// The region around P is not dense enough to create a new cluster, mark
// as noise for now.
@@ -119,11 +111,12 @@ void InstructionBenchmarkClustering::dbScan(const size_t MinPts) {
CurrentCluster.PointIndices.push_back(P);
// Process P's neighbors.
- std::unordered_set<size_t> ToProcess(Neighbors.begin(), Neighbors.end());
+ llvm::SetVector<size_t, std::deque<size_t>> ToProcess;
+ ToProcess.insert(Neighbors.begin(), Neighbors.end());
while (!ToProcess.empty()) {
// Retrieve a point from the set.
const size_t Q = *ToProcess.begin();
- ToProcess.erase(Q);
+ ToProcess.erase(ToProcess.begin());
if (ClusterIdForPoint_[Q].isNoise()) {
// Change noise point to border point.
@@ -138,12 +131,14 @@ void InstructionBenchmarkClustering::dbScan(const size_t MinPts) {
ClusterIdForPoint_[Q] = CurrentCluster.Id;
CurrentCluster.PointIndices.push_back(Q);
// And extend to the neighbors of Q if the region is dense enough.
- const auto Neighbors = rangeQuery(Q);
+ rangeQuery(Q, Neighbors);
if (Neighbors.size() + 1 >= MinPts) {
ToProcess.insert(Neighbors.begin(), Neighbors.end());
}
}
}
+ // assert(Neighbors.capacity() == (Points_.size() - 1));
+ // ^ True, but it is not quaranteed to be true in all the cases.
// Add noisy points to noise cluster.
for (size_t P = 0, NumPoints = Points_.size(); P < NumPoints; ++P) {
@@ -170,3 +165,4 @@ InstructionBenchmarkClustering::create(
}
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/Clustering.h b/tools/llvm-exegesis/lib/Clustering.h
index c811020e0fe8..932cbba2038e 100644
--- a/tools/llvm-exegesis/lib/Clustering.h
+++ b/tools/llvm-exegesis/lib/Clustering.h
@@ -19,6 +19,7 @@
#include "llvm/Support/Error.h"
#include <vector>
+namespace llvm {
namespace exegesis {
class InstructionBenchmarkClustering {
@@ -89,14 +90,21 @@ public:
// Returns true if the given point is within a distance Epsilon of each other.
bool isNeighbour(const std::vector<BenchmarkMeasure> &P,
- const std::vector<BenchmarkMeasure> &Q) const;
+ const std::vector<BenchmarkMeasure> &Q) const {
+ double DistanceSquared = 0.0;
+ for (size_t I = 0, E = P.size(); I < E; ++I) {
+ const auto Diff = P[I].PerInstructionValue - Q[I].PerInstructionValue;
+ DistanceSquared += Diff * Diff;
+ }
+ return DistanceSquared <= EpsilonSquared_;
+ }
private:
InstructionBenchmarkClustering(
const std::vector<InstructionBenchmark> &Points, double EpsilonSquared);
llvm::Error validateAndSetup();
void dbScan(size_t MinPts);
- std::vector<size_t> rangeQuery(size_t Q) const;
+ void rangeQuery(size_t Q, std::vector<size_t> &Scratchpad) const;
const std::vector<InstructionBenchmark> &Points_;
const double EpsilonSquared_;
@@ -109,5 +117,6 @@ private:
};
} // namespace exegesis
+} // namespace llvm
#endif // LLVM_TOOLS_LLVM_EXEGESIS_CLUSTERING_H
diff --git a/tools/llvm-exegesis/lib/CodeTemplate.cpp b/tools/llvm-exegesis/lib/CodeTemplate.cpp
new file mode 100644
index 000000000000..e159b000755a
--- /dev/null
+++ b/tools/llvm-exegesis/lib/CodeTemplate.cpp
@@ -0,0 +1,119 @@
+//===-- CodeTemplate.cpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CodeTemplate.h"
+
+namespace llvm {
+namespace exegesis {
+
+CodeTemplate::CodeTemplate(CodeTemplate &&) = default;
+
+CodeTemplate &CodeTemplate::operator=(CodeTemplate &&) = default;
+
+InstructionTemplate::InstructionTemplate(const Instruction &Instr)
+ : Instr(Instr), VariableValues(Instr.Variables.size()) {}
+
+InstructionTemplate::InstructionTemplate(InstructionTemplate &&) = default;
+
+InstructionTemplate &InstructionTemplate::
+operator=(InstructionTemplate &&) = default;
+
+InstructionTemplate::InstructionTemplate(const InstructionTemplate &) = default;
+
+InstructionTemplate &InstructionTemplate::
+operator=(const InstructionTemplate &) = default;
+
+unsigned InstructionTemplate::getOpcode() const {
+ return Instr.Description->getOpcode();
+}
+
+llvm::MCOperand &InstructionTemplate::getValueFor(const Variable &Var) {
+ return VariableValues[Var.getIndex()];
+}
+
+const llvm::MCOperand &
+InstructionTemplate::getValueFor(const Variable &Var) const {
+ return VariableValues[Var.getIndex()];
+}
+
+llvm::MCOperand &InstructionTemplate::getValueFor(const Operand &Op) {
+ return getValueFor(Instr.Variables[Op.getVariableIndex()]);
+}
+
+const llvm::MCOperand &
+InstructionTemplate::getValueFor(const Operand &Op) const {
+ return getValueFor(Instr.Variables[Op.getVariableIndex()]);
+}
+
+bool InstructionTemplate::hasImmediateVariables() const {
+ return llvm::any_of(Instr.Variables, [this](const Variable &Var) {
+ return Instr.getPrimaryOperand(Var).isImmediate();
+ });
+}
+
+llvm::MCInst InstructionTemplate::build() const {
+ llvm::MCInst Result;
+ Result.setOpcode(Instr.Description->Opcode);
+ for (const auto &Op : Instr.Operands)
+ if (Op.isExplicit())
+ Result.addOperand(getValueFor(Op));
+ return Result;
+}
+
+bool isEnumValue(ExecutionMode Execution) {
+ return llvm::isPowerOf2_32(static_cast<uint32_t>(Execution));
+}
+
+llvm::StringRef getName(ExecutionMode Bit) {
+ assert(isEnumValue(Bit) && "Bit must be a power of two");
+ switch (Bit) {
+ case ExecutionMode::UNKNOWN:
+ return "UNKNOWN";
+ case ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS:
+ return "ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS";
+ case ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS:
+ return "ALWAYS_SERIAL_TIED_REGS_ALIAS";
+ case ExecutionMode::SERIAL_VIA_MEMORY_INSTR:
+ return "SERIAL_VIA_MEMORY_INSTR";
+ case ExecutionMode::SERIAL_VIA_EXPLICIT_REGS:
+ return "SERIAL_VIA_EXPLICIT_REGS";
+ case ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR:
+ return "SERIAL_VIA_NON_MEMORY_INSTR";
+ case ExecutionMode::ALWAYS_PARALLEL_MISSING_USE_OR_DEF:
+ return "ALWAYS_PARALLEL_MISSING_USE_OR_DEF";
+ case ExecutionMode::PARALLEL_VIA_EXPLICIT_REGS:
+ return "PARALLEL_VIA_EXPLICIT_REGS";
+ }
+ llvm_unreachable("Missing enum case");
+}
+
+llvm::ArrayRef<ExecutionMode> getAllExecutionBits() {
+ static const ExecutionMode kAllExecutionModeBits[] = {
+ ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS,
+ ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS,
+ ExecutionMode::SERIAL_VIA_MEMORY_INSTR,
+ ExecutionMode::SERIAL_VIA_EXPLICIT_REGS,
+ ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR,
+ ExecutionMode::ALWAYS_PARALLEL_MISSING_USE_OR_DEF,
+ ExecutionMode::PARALLEL_VIA_EXPLICIT_REGS,
+ };
+ return llvm::makeArrayRef(kAllExecutionModeBits);
+}
+
+llvm::SmallVector<ExecutionMode, 4>
+getExecutionModeBits(ExecutionMode Execution) {
+ llvm::SmallVector<ExecutionMode, 4> Result;
+ for (const auto Bit : getAllExecutionBits())
+ if ((Execution & Bit) == Bit)
+ Result.push_back(Bit);
+ return Result;
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/CodeTemplate.h b/tools/llvm-exegesis/lib/CodeTemplate.h
new file mode 100644
index 000000000000..4c55487f3d12
--- /dev/null
+++ b/tools/llvm-exegesis/lib/CodeTemplate.h
@@ -0,0 +1,131 @@
+//===-- CodeTemplate.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// A set of structures and functions to craft instructions for the
+/// SnippetGenerator.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_CODETEMPLATE_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_CODETEMPLATE_H
+
+#include "MCInstrDescView.h"
+#include "llvm/ADT/BitmaskEnum.h"
+
+namespace llvm {
+namespace exegesis {
+
+// A template for an Instruction holding values for each of its Variables.
+struct InstructionTemplate {
+ InstructionTemplate(const Instruction &Instr);
+
+ InstructionTemplate(const InstructionTemplate &); // default
+ InstructionTemplate &operator=(const InstructionTemplate &); // default
+ InstructionTemplate(InstructionTemplate &&); // default
+ InstructionTemplate &operator=(InstructionTemplate &&); // default
+
+ unsigned getOpcode() const;
+ llvm::MCOperand &getValueFor(const Variable &Var);
+ const llvm::MCOperand &getValueFor(const Variable &Var) const;
+ llvm::MCOperand &getValueFor(const Operand &Op);
+ const llvm::MCOperand &getValueFor(const Operand &Op) const;
+ bool hasImmediateVariables() const;
+
+ // Builds an llvm::MCInst from this InstructionTemplate setting its operands
+ // to the corresponding variable values. Precondition: All VariableValues must
+ // be set.
+ llvm::MCInst build() const;
+
+ Instruction Instr;
+ llvm::SmallVector<llvm::MCOperand, 4> VariableValues;
+};
+
+enum class ExecutionMode : uint8_t {
+ UNKNOWN = 0U,
+ // The instruction is always serial because implicit Use and Def alias.
+ // e.g. AAA (alias via EFLAGS)
+ ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS = 1u << 0,
+
+ // The instruction is always serial because one Def is tied to a Use.
+ // e.g. AND32ri (alias via tied GR32)
+ ALWAYS_SERIAL_TIED_REGS_ALIAS = 1u << 1,
+
+ // The execution can be made serial by inserting a second instruction that
+ // clobbers/reads memory.
+ // e.g. MOV8rm
+ SERIAL_VIA_MEMORY_INSTR = 1u << 2,
+
+ // The execution can be made serial by picking one Def that aliases with one
+ // Use.
+ // e.g. VXORPSrr XMM1, XMM1, XMM2
+ SERIAL_VIA_EXPLICIT_REGS = 1u << 3,
+
+ // The execution can be made serial by inserting a second instruction that
+ // uses one of the Defs and defs one of the Uses.
+ // e.g.
+ // 1st instruction: MMX_PMOVMSKBrr ECX, MM7
+ // 2nd instruction: MMX_MOVD64rr MM7, ECX
+ // or instruction: MMX_MOVD64to64rr MM7, ECX
+ // or instruction: MMX_PINSRWrr MM7, MM7, ECX, 1
+ SERIAL_VIA_NON_MEMORY_INSTR = 1u << 4,
+
+ // The execution is always parallel because the instruction is missing Use or
+ // Def operands.
+ ALWAYS_PARALLEL_MISSING_USE_OR_DEF = 1u << 5,
+
+ // The execution can be made parallel by repeating the same instruction but
+ // making sure that Defs of one instruction do not alias with Uses of the
+ // second one.
+ PARALLEL_VIA_EXPLICIT_REGS = 1u << 6,
+
+ LLVM_MARK_AS_BITMASK_ENUM(/*Largest*/ PARALLEL_VIA_EXPLICIT_REGS)
+};
+
+// Returns whether Execution is one of the values defined in the enum above.
+bool isEnumValue(ExecutionMode Execution);
+
+// Returns a human readable string for the enum.
+llvm::StringRef getName(ExecutionMode Execution);
+
+// Returns a sequence of increasing powers of two corresponding to all the
+// Execution flags.
+llvm::ArrayRef<ExecutionMode> getAllExecutionBits();
+
+// Decomposes Execution into individual set bits.
+llvm::SmallVector<ExecutionMode, 4> getExecutionModeBits(ExecutionMode);
+
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+// A CodeTemplate is a set of InstructionTemplates that may not be fully
+// specified (i.e. some variables are not yet set). This allows the
+// SnippetGenerator to instantiate it many times with specific values to study
+// their impact on instruction's performance.
+struct CodeTemplate {
+ CodeTemplate() = default;
+
+ CodeTemplate(CodeTemplate &&); // default
+ CodeTemplate &operator=(CodeTemplate &&); // default
+ CodeTemplate(const CodeTemplate &) = delete;
+ CodeTemplate &operator=(const CodeTemplate &) = delete;
+
+ ExecutionMode Execution = ExecutionMode::UNKNOWN;
+ // Some information about how this template has been created.
+ std::string Info;
+ // The list of the instructions for this template.
+ std::vector<InstructionTemplate> Instructions;
+ // If the template uses the provided scratch memory, the register in which
+ // the pointer to this memory is passed in to the function.
+ unsigned ScratchSpacePointerInReg = 0;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_CODETEMPLATE_H
diff --git a/tools/llvm-exegesis/lib/Latency.cpp b/tools/llvm-exegesis/lib/Latency.cpp
index e2aae970e226..9a56b275088b 100644
--- a/tools/llvm-exegesis/lib/Latency.cpp
+++ b/tools/llvm-exegesis/lib/Latency.cpp
@@ -13,122 +13,182 @@
#include "BenchmarkRunner.h"
#include "MCInstrDescView.h"
#include "PerfHelper.h"
+#include "Target.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstBuilder.h"
#include "llvm/Support/FormatVariadic.h"
+namespace llvm {
namespace exegesis {
-static bool hasUnknownOperand(const llvm::MCOperandInfo &OpInfo) {
- return OpInfo.OperandType == llvm::MCOI::OPERAND_UNKNOWN;
-}
-
-// FIXME: Handle memory, see PR36905.
-static bool hasMemoryOperand(const llvm::MCOperandInfo &OpInfo) {
- return OpInfo.OperandType == llvm::MCOI::OPERAND_MEMORY;
-}
+struct ExecutionClass {
+ ExecutionMode Mask;
+ const char *Description;
+} static const kExecutionClasses[] = {
+ {ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS |
+ ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS,
+ "Repeating a single implicitly serial instruction"},
+ {ExecutionMode::SERIAL_VIA_EXPLICIT_REGS,
+ "Repeating a single explicitly serial instruction"},
+ {ExecutionMode::SERIAL_VIA_MEMORY_INSTR |
+ ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR,
+ "Repeating two instructions"},
+};
-LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
-
-llvm::Error LatencyBenchmarkRunner::isInfeasible(
- const llvm::MCInstrDesc &MCInstrDesc) const {
- if (llvm::any_of(MCInstrDesc.operands(), hasUnknownOperand))
- return llvm::make_error<BenchmarkFailure>(
- "Infeasible : has unknown operands");
- if (llvm::any_of(MCInstrDesc.operands(), hasMemoryOperand))
- return llvm::make_error<BenchmarkFailure>(
- "Infeasible : has memory operands");
- return llvm::Error::success();
-}
+static constexpr size_t kMaxAliasingInstructions = 10;
-llvm::Expected<SnippetPrototype>
-LatencyBenchmarkRunner::generateTwoInstructionPrototype(
- const Instruction &Instr) const {
+static std::vector<Instruction>
+computeAliasingInstructions(const LLVMState &State, const Instruction &Instr,
+ size_t MaxAliasingInstructions) {
+ // Randomly iterate the set of instructions.
std::vector<unsigned> Opcodes;
Opcodes.resize(State.getInstrInfo().getNumOpcodes());
std::iota(Opcodes.begin(), Opcodes.end(), 0U);
std::shuffle(Opcodes.begin(), Opcodes.end(), randomGenerator());
+
+ std::vector<Instruction> AliasingInstructions;
for (const unsigned OtherOpcode : Opcodes) {
- if (OtherOpcode == Instr.Description->Opcode)
+ if (OtherOpcode == Instr.Description->getOpcode())
continue;
- const auto &OtherInstrDesc = State.getInstrInfo().get(OtherOpcode);
- if (auto E = isInfeasible(OtherInstrDesc)) {
- llvm::consumeError(std::move(E));
- continue;
- }
- const Instruction OtherInstr(OtherInstrDesc, RATC);
- const AliasingConfigurations Forward(Instr, OtherInstr);
- const AliasingConfigurations Back(OtherInstr, Instr);
- if (Forward.empty() || Back.empty())
+ const Instruction &OtherInstr = State.getIC().getInstr(OtherOpcode);
+ if (OtherInstr.hasMemoryOperands())
continue;
- InstructionInstance ThisII(Instr);
- InstructionInstance OtherII(OtherInstr);
- if (!Forward.hasImplicitAliasing())
- setRandomAliasing(Forward, ThisII, OtherII);
- if (!Back.hasImplicitAliasing())
- setRandomAliasing(Back, OtherII, ThisII);
- SnippetPrototype Prototype;
- Prototype.Explanation =
- llvm::formatv("creating cycle through {0}.",
- State.getInstrInfo().getName(OtherOpcode));
- Prototype.Snippet.push_back(std::move(ThisII));
- Prototype.Snippet.push_back(std::move(OtherII));
- return std::move(Prototype);
+ if (Instr.hasAliasingRegistersThrough(OtherInstr))
+ AliasingInstructions.push_back(std::move(OtherInstr));
+ if (AliasingInstructions.size() >= MaxAliasingInstructions)
+ break;
}
- return llvm::make_error<BenchmarkFailure>(
- "Infeasible : Didn't find any scheme to make the instruction serial");
+ return AliasingInstructions;
}
-llvm::Expected<SnippetPrototype>
-LatencyBenchmarkRunner::generatePrototype(unsigned Opcode) const {
- const auto &InstrDesc = State.getInstrInfo().get(Opcode);
- if (auto E = isInfeasible(InstrDesc))
- return std::move(E);
- const Instruction Instr(InstrDesc, RATC);
- if (auto SelfAliasingPrototype = generateSelfAliasingPrototype(Instr))
- return SelfAliasingPrototype;
- else
- llvm::consumeError(SelfAliasingPrototype.takeError());
- // No self aliasing, trying to create a dependency through another opcode.
- return generateTwoInstructionPrototype(Instr);
+static ExecutionMode getExecutionModes(const Instruction &Instr) {
+ ExecutionMode EM = ExecutionMode::UNKNOWN;
+ if (Instr.hasAliasingImplicitRegisters())
+ EM |= ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS;
+ if (Instr.hasTiedRegisters())
+ EM |= ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS;
+ if (Instr.hasMemoryOperands())
+ EM |= ExecutionMode::SERIAL_VIA_MEMORY_INSTR;
+ else {
+ if (Instr.hasAliasingRegisters())
+ EM |= ExecutionMode::SERIAL_VIA_EXPLICIT_REGS;
+ if (Instr.hasOneUseOrOneDef())
+ EM |= ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR;
+ }
+ return EM;
}
-const char *LatencyBenchmarkRunner::getCounterName() const {
- if (!State.getSubtargetInfo().getSchedModel().hasExtraProcessorInfo())
- llvm::report_fatal_error("sched model is missing extra processor info!");
- const char *CounterName = State.getSubtargetInfo()
- .getSchedModel()
- .getExtraProcessorInfo()
- .PfmCounters.CycleCounter;
- if (!CounterName)
- llvm::report_fatal_error("sched model does not define a cycle counter");
- return CounterName;
+static void appendCodeTemplates(const LLVMState &State,
+ const Instruction &Instr,
+ ExecutionMode ExecutionModeBit,
+ llvm::StringRef ExecutionClassDescription,
+ std::vector<CodeTemplate> &CodeTemplates) {
+ assert(isEnumValue(ExecutionModeBit) && "Bit must be a power of two");
+ switch (ExecutionModeBit) {
+ case ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS:
+ // Nothing to do, the instruction is always serial.
+ LLVM_FALLTHROUGH;
+ case ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS: {
+ // Picking whatever value for the tied variable will make the instruction
+ // serial.
+ CodeTemplate CT;
+ CT.Execution = ExecutionModeBit;
+ CT.Info = ExecutionClassDescription;
+ CT.Instructions.push_back(Instr);
+ CodeTemplates.push_back(std::move(CT));
+ return;
+ }
+ case ExecutionMode::SERIAL_VIA_MEMORY_INSTR: {
+ // Select back-to-back memory instruction.
+ // TODO: Implement me.
+ return;
+ }
+ case ExecutionMode::SERIAL_VIA_EXPLICIT_REGS: {
+ // Making the execution of this instruction serial by selecting one def
+ // register to alias with one use register.
+ const AliasingConfigurations SelfAliasing(Instr, Instr);
+ assert(!SelfAliasing.empty() && !SelfAliasing.hasImplicitAliasing() &&
+ "Instr must alias itself explicitly");
+ InstructionTemplate IT(Instr);
+ // This is a self aliasing instruction so defs and uses are from the same
+ // instance, hence twice IT in the following call.
+ setRandomAliasing(SelfAliasing, IT, IT);
+ CodeTemplate CT;
+ CT.Execution = ExecutionModeBit;
+ CT.Info = ExecutionClassDescription;
+ CT.Instructions.push_back(std::move(IT));
+ CodeTemplates.push_back(std::move(CT));
+ return;
+ }
+ case ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR: {
+ // Select back-to-back non-memory instruction.
+ for (const auto OtherInstr :
+ computeAliasingInstructions(State, Instr, kMaxAliasingInstructions)) {
+ const AliasingConfigurations Forward(Instr, OtherInstr);
+ const AliasingConfigurations Back(OtherInstr, Instr);
+ InstructionTemplate ThisIT(Instr);
+ InstructionTemplate OtherIT(OtherInstr);
+ if (!Forward.hasImplicitAliasing())
+ setRandomAliasing(Forward, ThisIT, OtherIT);
+ if (!Back.hasImplicitAliasing())
+ setRandomAliasing(Back, OtherIT, ThisIT);
+ CodeTemplate CT;
+ CT.Execution = ExecutionModeBit;
+ CT.Info = ExecutionClassDescription;
+ CT.Instructions.push_back(std::move(ThisIT));
+ CT.Instructions.push_back(std::move(OtherIT));
+ CodeTemplates.push_back(std::move(CT));
+ }
+ return;
+ }
+ default:
+ llvm_unreachable("Unhandled enum value");
+ }
+}
+
+LatencySnippetGenerator::~LatencySnippetGenerator() = default;
+
+llvm::Expected<std::vector<CodeTemplate>>
+LatencySnippetGenerator::generateCodeTemplates(const Instruction &Instr) const {
+ std::vector<CodeTemplate> Results;
+ const ExecutionMode EM = getExecutionModes(Instr);
+ for (const auto EC : kExecutionClasses) {
+ for (const auto ExecutionModeBit : getExecutionModeBits(EM & EC.Mask))
+ appendCodeTemplates(State, Instr, ExecutionModeBit, EC.Description,
+ Results);
+ if (!Results.empty())
+ break;
+ }
+ if (Results.empty())
+ return llvm::make_error<BenchmarkFailure>(
+ "No strategy found to make the execution serial");
+ return std::move(Results);
}
-std::vector<BenchmarkMeasure>
-LatencyBenchmarkRunner::runMeasurements(const ExecutableFunction &Function,
- const unsigned NumRepetitions) const {
+LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
+
+llvm::Expected<std::vector<BenchmarkMeasure>>
+LatencyBenchmarkRunner::runMeasurements(
+ const FunctionExecutor &Executor) const {
// Cycle measurements include some overhead from the kernel. Repeat the
// measure several times and take the minimum value.
constexpr const int NumMeasurements = 30;
- int64_t MinLatency = std::numeric_limits<int64_t>::max();
- const char *CounterName = getCounterName();
+ int64_t MinValue = std::numeric_limits<int64_t>::max();
+ const char *CounterName = State.getPfmCounters().CycleCounter;
if (!CounterName)
- llvm::report_fatal_error("could not determine cycle counter name");
- const pfm::PerfEvent CyclesPerfEvent(CounterName);
- if (!CyclesPerfEvent.valid())
- llvm::report_fatal_error("invalid perf event");
+ llvm::report_fatal_error("sched model does not define a cycle counter");
for (size_t I = 0; I < NumMeasurements; ++I) {
- pfm::Counter Counter(CyclesPerfEvent);
- Counter.start();
- Function();
- Counter.stop();
- const int64_t Value = Counter.read();
- if (Value < MinLatency)
- MinLatency = Value;
+ auto ExpectedCounterValue = Executor.runAndMeasure(CounterName);
+ if (!ExpectedCounterValue)
+ return ExpectedCounterValue.takeError();
+ if (*ExpectedCounterValue < MinValue)
+ MinValue = *ExpectedCounterValue;
}
- return {{"latency", static_cast<double>(MinLatency) / NumRepetitions, ""}};
+ std::vector<BenchmarkMeasure> Result = {
+ BenchmarkMeasure::Create("latency", MinValue)};
+ return std::move(Result);
}
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/Latency.h b/tools/llvm-exegesis/lib/Latency.h
index 9d6cfc7b387d..513c5f3ce780 100644
--- a/tools/llvm-exegesis/lib/Latency.h
+++ b/tools/llvm-exegesis/lib/Latency.h
@@ -17,31 +17,31 @@
#include "BenchmarkRunner.h"
#include "MCInstrDescView.h"
+#include "SnippetGenerator.h"
+namespace llvm {
namespace exegesis {
+class LatencySnippetGenerator : public SnippetGenerator {
+public:
+ LatencySnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {}
+ ~LatencySnippetGenerator() override;
+
+ llvm::Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(const Instruction &Instr) const override;
+};
+
class LatencyBenchmarkRunner : public BenchmarkRunner {
public:
LatencyBenchmarkRunner(const LLVMState &State)
: BenchmarkRunner(State, InstructionBenchmark::Latency) {}
~LatencyBenchmarkRunner() override;
- llvm::Expected<SnippetPrototype>
- generatePrototype(unsigned Opcode) const override;
-
private:
- llvm::Error isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const;
-
- llvm::Expected<SnippetPrototype> generateTwoInstructionPrototype(
- const Instruction &Instr) const;
-
- std::vector<BenchmarkMeasure>
- runMeasurements(const ExecutableFunction &EF,
- const unsigned NumRepetitions) const override;
-
- virtual const char *getCounterName() const;
+ llvm::Expected<std::vector<BenchmarkMeasure>>
+ runMeasurements(const FunctionExecutor &Executor) const override;
};
-
} // namespace exegesis
+} // namespace llvm
#endif // LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H
diff --git a/tools/llvm-exegesis/lib/LlvmState.cpp b/tools/llvm-exegesis/lib/LlvmState.cpp
index 9ff42ca71fd2..cc01f1fa4ca5 100644
--- a/tools/llvm-exegesis/lib/LlvmState.cpp
+++ b/tools/llvm-exegesis/lib/LlvmState.cpp
@@ -19,27 +19,35 @@
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
+namespace llvm {
namespace exegesis {
-LLVMState::LLVMState(const std::string &Triple, const std::string &CpuName) {
+LLVMState::LLVMState(const std::string &Triple, const std::string &CpuName,
+ const std::string &Features) {
std::string Error;
const llvm::Target *const TheTarget =
llvm::TargetRegistry::lookupTarget(Triple, Error);
assert(TheTarget && "unknown target for host");
const llvm::TargetOptions Options;
- TargetMachine.reset(static_cast<llvm::LLVMTargetMachine *>(
- TheTarget->createTargetMachine(Triple, CpuName, /*Features*/ "", Options,
- llvm::Reloc::Model::Static)));
+ TargetMachine.reset(
+ static_cast<llvm::LLVMTargetMachine *>(TheTarget->createTargetMachine(
+ Triple, CpuName, Features, Options, llvm::Reloc::Model::Static)));
TheExegesisTarget = ExegesisTarget::lookup(TargetMachine->getTargetTriple());
if (!TheExegesisTarget) {
llvm::errs() << "no exegesis target for " << Triple << ", using default\n";
TheExegesisTarget = &ExegesisTarget::getDefault();
}
+ PfmCounters = &TheExegesisTarget->getPfmCounters(CpuName);
+
+ RATC.reset(new RegisterAliasingTrackerCache(
+ getRegInfo(), getFunctionReservedRegs(getTargetMachine())));
+ IC.reset(new InstructionsCache(getInstrInfo(), getRATC()));
}
-LLVMState::LLVMState()
+LLVMState::LLVMState(const std::string &CpuName)
: LLVMState(llvm::sys::getProcessTriple(),
- llvm::sys::getHostCPUName().str()) {}
+ CpuName.empty() ? llvm::sys::getHostCPUName().str() : CpuName,
+ "") {}
std::unique_ptr<llvm::LLVMTargetMachine>
LLVMState::createTargetMachine() const {
@@ -69,3 +77,4 @@ bool LLVMState::canAssemble(const llvm::MCInst &Inst) const {
}
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/LlvmState.h b/tools/llvm-exegesis/lib/LlvmState.h
index c84db300841e..e4f12a3f858e 100644
--- a/tools/llvm-exegesis/lib/LlvmState.h
+++ b/tools/llvm-exegesis/lib/LlvmState.h
@@ -15,6 +15,8 @@
#ifndef LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H
#define LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H
+#include "MCInstrDescView.h"
+#include "RegisterAliasing.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrInfo.h"
@@ -24,18 +26,22 @@
#include <memory>
#include <string>
+namespace llvm {
namespace exegesis {
class ExegesisTarget;
+struct PfmCountersInfo;
// An object to initialize LLVM and prepare objects needed to run the
// measurements.
class LLVMState {
public:
- LLVMState();
+ // Uses the host triple. If CpuName is empty, uses the host CPU.
+ LLVMState(const std::string &CpuName);
LLVMState(const std::string &Triple,
- const std::string &CpuName); // For tests.
+ const std::string &CpuName,
+ const std::string &Features = ""); // For tests.
const llvm::TargetMachine &getTargetMachine() const { return *TargetMachine; }
std::unique_ptr<llvm::LLVMTargetMachine> createTargetMachine() const;
@@ -55,11 +61,20 @@ public:
return *TargetMachine->getMCSubtargetInfo();
}
+ const RegisterAliasingTrackerCache &getRATC() const { return *RATC; }
+ const InstructionsCache &getIC() const { return *IC; }
+
+ const PfmCountersInfo &getPfmCounters() const { return *PfmCounters; }
+
private:
const ExegesisTarget *TheExegesisTarget;
std::unique_ptr<const llvm::TargetMachine> TargetMachine;
+ std::unique_ptr<const RegisterAliasingTrackerCache> RATC;
+ std::unique_ptr<const InstructionsCache> IC;
+ const PfmCountersInfo *PfmCounters;
};
} // namespace exegesis
+} // namespace llvm
#endif // LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H
diff --git a/tools/llvm-exegesis/lib/MCInstrDescView.cpp b/tools/llvm-exegesis/lib/MCInstrDescView.cpp
index fc0d5b327b80..e0521af4d19e 100644
--- a/tools/llvm-exegesis/lib/MCInstrDescView.cpp
+++ b/tools/llvm-exegesis/lib/MCInstrDescView.cpp
@@ -15,42 +15,118 @@
#include "llvm/ADT/STLExtras.h"
+namespace llvm {
namespace exegesis {
-Instruction::Instruction(const llvm::MCInstrDesc &MCInstrDesc,
- const RegisterAliasingTrackerCache &RATC)
- : Description(&MCInstrDesc) {
+unsigned Variable::getIndex() const {
+ assert(Index >= 0 && "Index must be set");
+ return Index;
+}
+
+unsigned Variable::getPrimaryOperandIndex() const {
+ assert(!TiedOperands.empty());
+ return TiedOperands[0];
+}
+
+bool Variable::hasTiedOperands() const {
+ assert(TiedOperands.size() <= 2 &&
+ "No more than two operands can be tied together");
+ // By definition only Use and Def operands can be tied together.
+ // TiedOperands[0] is the Def operand (LLVM stores defs first).
+ // TiedOperands[1] is the Use operand.
+ return TiedOperands.size() > 1;
+}
+
+unsigned Operand::getIndex() const {
+ assert(Index >= 0 && "Index must be set");
+ return Index;
+}
+
+bool Operand::isExplicit() const { return Info; }
+
+bool Operand::isImplicit() const { return !Info; }
+
+bool Operand::isImplicitReg() const { return ImplicitReg; }
+
+bool Operand::isDef() const { return IsDef; }
+
+bool Operand::isUse() const { return !IsDef; }
+
+bool Operand::isReg() const { return Tracker; }
+
+bool Operand::isTied() const { return TiedToIndex >= 0; }
+
+bool Operand::isVariable() const { return VariableIndex >= 0; }
+
+bool Operand::isMemory() const {
+ return isExplicit() &&
+ getExplicitOperandInfo().OperandType == llvm::MCOI::OPERAND_MEMORY;
+}
+
+bool Operand::isImmediate() const {
+ return isExplicit() &&
+ getExplicitOperandInfo().OperandType == llvm::MCOI::OPERAND_IMMEDIATE;
+}
+
+unsigned Operand::getTiedToIndex() const {
+ assert(isTied() && "Operand must be tied to get the tied index");
+ assert(TiedToIndex >= 0 && "TiedToIndex must be set");
+ return TiedToIndex;
+}
+
+unsigned Operand::getVariableIndex() const {
+ assert(isVariable() && "Operand must be variable to get the Variable index");
+ assert(VariableIndex >= 0 && "VariableIndex must be set");
+ return VariableIndex;
+}
+
+unsigned Operand::getImplicitReg() const {
+ assert(ImplicitReg);
+ return *ImplicitReg;
+}
+
+const RegisterAliasingTracker &Operand::getRegisterAliasing() const {
+ assert(Tracker);
+ return *Tracker;
+}
+
+const llvm::MCOperandInfo &Operand::getExplicitOperandInfo() const {
+ assert(Info);
+ return *Info;
+}
+
+Instruction::Instruction(const llvm::MCInstrInfo &InstrInfo,
+ const RegisterAliasingTrackerCache &RATC,
+ unsigned Opcode)
+ : Description(&InstrInfo.get(Opcode)), Name(InstrInfo.getName(Opcode)) {
unsigned OpIndex = 0;
- for (; OpIndex < MCInstrDesc.getNumOperands(); ++OpIndex) {
- const auto &OpInfo = MCInstrDesc.opInfo_begin()[OpIndex];
+ for (; OpIndex < Description->getNumOperands(); ++OpIndex) {
+ const auto &OpInfo = Description->opInfo_begin()[OpIndex];
Operand Operand;
Operand.Index = OpIndex;
- Operand.IsDef = (OpIndex < MCInstrDesc.getNumDefs());
- Operand.IsExplicit = true;
+ Operand.IsDef = (OpIndex < Description->getNumDefs());
// TODO(gchatelet): Handle isLookupPtrRegClass.
if (OpInfo.RegClass >= 0)
Operand.Tracker = &RATC.getRegisterClass(OpInfo.RegClass);
Operand.TiedToIndex =
- MCInstrDesc.getOperandConstraint(OpIndex, llvm::MCOI::TIED_TO);
+ Description->getOperandConstraint(OpIndex, llvm::MCOI::TIED_TO);
Operand.Info = &OpInfo;
Operands.push_back(Operand);
}
- for (const llvm::MCPhysReg *MCPhysReg = MCInstrDesc.getImplicitDefs();
+ for (const llvm::MCPhysReg *MCPhysReg = Description->getImplicitDefs();
MCPhysReg && *MCPhysReg; ++MCPhysReg, ++OpIndex) {
Operand Operand;
Operand.Index = OpIndex;
Operand.IsDef = true;
- Operand.IsExplicit = false;
Operand.Tracker = &RATC.getRegister(*MCPhysReg);
Operand.ImplicitReg = MCPhysReg;
Operands.push_back(Operand);
}
- for (const llvm::MCPhysReg *MCPhysReg = MCInstrDesc.getImplicitUses();
+ for (const llvm::MCPhysReg *MCPhysReg = Description->getImplicitUses();
MCPhysReg && *MCPhysReg; ++MCPhysReg, ++OpIndex) {
Operand Operand;
Operand.Index = OpIndex;
Operand.IsDef = false;
- Operand.IsExplicit = false;
Operand.Tracker = &RATC.getRegister(*MCPhysReg);
Operand.ImplicitReg = MCPhysReg;
Operands.push_back(Operand);
@@ -58,7 +134,7 @@ Instruction::Instruction(const llvm::MCInstrDesc &MCInstrDesc,
// Assigning Variables to non tied explicit operands.
Variables.reserve(Operands.size()); // Variables.size() <= Operands.size()
for (auto &Op : Operands)
- if (Op.IsExplicit && Op.TiedToIndex < 0) {
+ if (Op.isExplicit() && !Op.isTied()) {
const size_t VariableIndex = Variables.size();
Op.VariableIndex = VariableIndex;
Variables.emplace_back();
@@ -66,89 +142,136 @@ Instruction::Instruction(const llvm::MCInstrDesc &MCInstrDesc,
}
// Assigning Variables to tied operands.
for (auto &Op : Operands)
- if (Op.TiedToIndex >= 0)
- Op.VariableIndex = Operands[Op.TiedToIndex].VariableIndex;
+ if (Op.isTied())
+ Op.VariableIndex = Operands[Op.getTiedToIndex()].getVariableIndex();
// Assigning Operands to Variables.
for (auto &Op : Operands)
- if (Op.VariableIndex >= 0)
- Variables[Op.VariableIndex].TiedOperands.push_back(Op.Index);
+ if (Op.isVariable())
+ Variables[Op.getVariableIndex()].TiedOperands.push_back(Op.getIndex());
// Processing Aliasing.
- DefRegisters = RATC.emptyRegisters();
- UseRegisters = RATC.emptyRegisters();
+ ImplDefRegs = RATC.emptyRegisters();
+ ImplUseRegs = RATC.emptyRegisters();
+ AllDefRegs = RATC.emptyRegisters();
+ AllUseRegs = RATC.emptyRegisters();
for (const auto &Op : Operands) {
- if (Op.Tracker) {
- auto &Registers = Op.IsDef ? DefRegisters : UseRegisters;
- Registers |= Op.Tracker->aliasedBits();
+ if (Op.isReg()) {
+ const auto &AliasingBits = Op.getRegisterAliasing().aliasedBits();
+ if (Op.isDef())
+ AllDefRegs |= AliasingBits;
+ if (Op.isUse())
+ AllUseRegs |= AliasingBits;
+ if (Op.isDef() && Op.isImplicit())
+ ImplDefRegs |= AliasingBits;
+ if (Op.isUse() && Op.isImplicit())
+ ImplUseRegs |= AliasingBits;
}
}
}
-InstructionInstance::InstructionInstance(const Instruction &Instr)
- : Instr(Instr), VariableValues(Instr.Variables.size()) {}
-
-InstructionInstance::InstructionInstance(InstructionInstance &&) = default;
-
-InstructionInstance &InstructionInstance::
-operator=(InstructionInstance &&) = default;
+const Operand &Instruction::getPrimaryOperand(const Variable &Var) const {
+ const auto PrimaryOperandIndex = Var.getPrimaryOperandIndex();
+ assert(PrimaryOperandIndex < Operands.size());
+ return Operands[PrimaryOperandIndex];
+}
-unsigned InstructionInstance::getOpcode() const {
- return Instr.Description->getOpcode();
+bool Instruction::hasMemoryOperands() const {
+ return any_of(Operands, [](const Operand &Op) {
+ return Op.isReg() && Op.isExplicit() && Op.isMemory();
+ });
}
-llvm::MCOperand &InstructionInstance::getValueFor(const Variable &Var) {
- return VariableValues[Var.Index];
+bool Instruction::hasAliasingImplicitRegisters() const {
+ return ImplDefRegs.anyCommon(ImplUseRegs);
}
-const llvm::MCOperand &
-InstructionInstance::getValueFor(const Variable &Var) const {
- return VariableValues[Var.Index];
+bool Instruction::hasAliasingImplicitRegistersThrough(
+ const Instruction &OtherInstr) const {
+ return ImplDefRegs.anyCommon(OtherInstr.ImplUseRegs) &&
+ OtherInstr.ImplDefRegs.anyCommon(ImplUseRegs);
}
-llvm::MCOperand &InstructionInstance::getValueFor(const Operand &Op) {
- assert(Op.VariableIndex >= 0);
- return getValueFor(Instr.Variables[Op.VariableIndex]);
+bool Instruction::hasAliasingRegistersThrough(
+ const Instruction &OtherInstr) const {
+ return AllDefRegs.anyCommon(OtherInstr.AllUseRegs) &&
+ OtherInstr.AllDefRegs.anyCommon(AllUseRegs);
}
-const llvm::MCOperand &
-InstructionInstance::getValueFor(const Operand &Op) const {
- assert(Op.VariableIndex >= 0);
- return getValueFor(Instr.Variables[Op.VariableIndex]);
+bool Instruction::hasTiedRegisters() const {
+ return llvm::any_of(
+ Variables, [](const Variable &Var) { return Var.hasTiedOperands(); });
}
-// forward declaration.
-static void randomize(const Instruction &Instr, const Variable &Var,
- llvm::MCOperand &AssignedValue);
-
-bool InstructionInstance::hasImmediateVariables() const {
- return llvm::any_of(Instr.Variables, [this](const Variable &Var) {
- assert(!Var.TiedOperands.empty());
- const unsigned OpIndex = Var.TiedOperands[0];
- const Operand &Op = Instr.Operands[OpIndex];
- assert(Op.Info);
- return Op.Info->OperandType == llvm::MCOI::OPERAND_IMMEDIATE;
- });
+bool Instruction::hasAliasingRegisters() const {
+ return AllDefRegs.anyCommon(AllUseRegs);
}
-void InstructionInstance::randomizeUnsetVariables() {
- for (const Variable &Var : Instr.Variables) {
- llvm::MCOperand &AssignedValue = getValueFor(Var);
- if (!AssignedValue.isValid())
- randomize(Instr, Var, AssignedValue);
- }
+bool Instruction::hasOneUseOrOneDef() const {
+ return AllDefRegs.count() || AllUseRegs.count();
}
-llvm::MCInst InstructionInstance::build() const {
- llvm::MCInst Result;
- Result.setOpcode(Instr.Description->Opcode);
- for (const auto &Op : Instr.Operands)
- if (Op.IsExplicit)
- Result.addOperand(getValueFor(Op));
- return Result;
+void Instruction::dump(const llvm::MCRegisterInfo &RegInfo,
+ llvm::raw_ostream &Stream) const {
+ Stream << "- " << Name << "\n";
+ for (const auto &Op : Operands) {
+ Stream << "- Op" << Op.getIndex();
+ if (Op.isExplicit())
+ Stream << " Explicit";
+ if (Op.isImplicit())
+ Stream << " Implicit";
+ if (Op.isUse())
+ Stream << " Use";
+ if (Op.isDef())
+ Stream << " Def";
+ if (Op.isImmediate())
+ Stream << " Immediate";
+ if (Op.isMemory())
+ Stream << " Memory";
+ if (Op.isReg()) {
+ if (Op.isImplicitReg())
+ Stream << " Reg(" << RegInfo.getName(Op.getImplicitReg()) << ")";
+ else
+ Stream << " RegClass("
+ << RegInfo.getRegClassName(
+ &RegInfo.getRegClass(Op.Info->RegClass))
+ << ")";
+ }
+ if (Op.isTied())
+ Stream << " TiedToOp" << Op.getTiedToIndex();
+ Stream << "\n";
+ }
+ for (const auto &Var : Variables) {
+ Stream << "- Var" << Var.getIndex();
+ Stream << " [";
+ bool IsFirst = true;
+ for (auto OperandIndex : Var.TiedOperands) {
+ if (!IsFirst)
+ Stream << ",";
+ Stream << "Op" << OperandIndex;
+ IsFirst = false;
+ }
+ Stream << "]";
+ Stream << "\n";
+ }
+ if (hasMemoryOperands())
+ Stream << "- hasMemoryOperands\n";
+ if (hasAliasingImplicitRegisters())
+ Stream << "- hasAliasingImplicitRegisters (execution is always serial)\n";
+ if (hasTiedRegisters())
+ Stream << "- hasTiedRegisters (execution is always serial)\n";
+ if (hasAliasingRegisters())
+ Stream << "- hasAliasingRegisters\n";
}
-SnippetPrototype::SnippetPrototype(SnippetPrototype &&) = default;
+InstructionsCache::InstructionsCache(const llvm::MCInstrInfo &InstrInfo,
+ const RegisterAliasingTrackerCache &RATC)
+ : InstrInfo(InstrInfo), RATC(RATC) {}
-SnippetPrototype &SnippetPrototype::operator=(SnippetPrototype &&) = default;
+const Instruction &InstructionsCache::getInstr(unsigned Opcode) const {
+ auto &Found = Instructions[Opcode];
+ if (!Found)
+ Found.reset(new Instruction(InstrInfo, RATC, Opcode));
+ return *Found;
+}
bool RegisterOperandAssignment::
operator==(const RegisterOperandAssignment &Other) const {
@@ -164,8 +287,8 @@ static void addOperandIfAlias(
const llvm::MCPhysReg Reg, bool SelectDef, llvm::ArrayRef<Operand> Operands,
llvm::SmallVectorImpl<RegisterOperandAssignment> &OperandValues) {
for (const auto &Op : Operands) {
- if (Op.Tracker && Op.IsDef == SelectDef) {
- const int SourceReg = Op.Tracker->getOrigin(Reg);
+ if (Op.isReg() && Op.isDef() == SelectDef) {
+ const int SourceReg = Op.getRegisterAliasing().getOrigin(Reg);
if (SourceReg >= 0)
OperandValues.emplace_back(&Op, SourceReg);
}
@@ -174,7 +297,7 @@ static void addOperandIfAlias(
bool AliasingRegisterOperands::hasImplicitAliasing() const {
const auto HasImplicit = [](const RegisterOperandAssignment &ROV) {
- return !ROV.Op->IsExplicit;
+ return ROV.Op->isImplicit();
};
return llvm::any_of(Defs, HasImplicit) && llvm::any_of(Uses, HasImplicit);
}
@@ -188,11 +311,10 @@ bool AliasingConfigurations::hasImplicitAliasing() const {
}
AliasingConfigurations::AliasingConfigurations(
- const Instruction &DefInstruction, const Instruction &UseInstruction)
- : DefInstruction(DefInstruction), UseInstruction(UseInstruction) {
- if (UseInstruction.UseRegisters.anyCommon(DefInstruction.DefRegisters)) {
- auto CommonRegisters = UseInstruction.UseRegisters;
- CommonRegisters &= DefInstruction.DefRegisters;
+ const Instruction &DefInstruction, const Instruction &UseInstruction) {
+ if (UseInstruction.AllUseRegs.anyCommon(DefInstruction.AllDefRegs)) {
+ auto CommonRegisters = UseInstruction.AllUseRegs;
+ CommonRegisters &= DefInstruction.AllDefRegs;
for (const llvm::MCPhysReg Reg : CommonRegisters.set_bits()) {
AliasingRegisterOperands ARO;
addOperandIfAlias(Reg, true, DefInstruction.Operands, ARO.Defs);
@@ -204,78 +326,6 @@ AliasingConfigurations::AliasingConfigurations(
}
}
-std::mt19937 &randomGenerator() {
- static std::random_device RandomDevice;
- static std::mt19937 RandomGenerator(RandomDevice());
- return RandomGenerator;
-}
-
-static size_t randomIndex(size_t Size) {
- assert(Size > 0);
- std::uniform_int_distribution<> Distribution(0, Size - 1);
- return Distribution(randomGenerator());
-}
-
-template <typename C>
-static auto randomElement(const C &Container) -> decltype(Container[0]) {
- return Container[randomIndex(Container.size())];
-}
-
-static void randomize(const Instruction &Instr, const Variable &Var,
- llvm::MCOperand &AssignedValue) {
- assert(!Var.TiedOperands.empty());
- const Operand &Op = Instr.Operands[Var.TiedOperands.front()];
- assert(Op.Info != nullptr);
- const auto &OpInfo = *Op.Info;
- switch (OpInfo.OperandType) {
- case llvm::MCOI::OperandType::OPERAND_IMMEDIATE:
- // FIXME: explore immediate values too.
- AssignedValue = llvm::MCOperand::createImm(1);
- break;
- case llvm::MCOI::OperandType::OPERAND_REGISTER: {
- assert(Op.Tracker);
- const auto &Registers = Op.Tracker->sourceBits();
- AssignedValue = llvm::MCOperand::createReg(randomBit(Registers));
- break;
- }
- default:
- break;
- }
-}
-
-static void setRegisterOperandValue(const RegisterOperandAssignment &ROV,
- InstructionInstance &II) {
- assert(ROV.Op);
- if (ROV.Op->IsExplicit) {
- auto &AssignedValue = II.getValueFor(*ROV.Op);
- if (AssignedValue.isValid()) {
- assert(AssignedValue.isReg() && AssignedValue.getReg() == ROV.Reg);
- return;
- }
- AssignedValue = llvm::MCOperand::createReg(ROV.Reg);
- } else {
- assert(ROV.Op->ImplicitReg != nullptr);
- assert(ROV.Reg == *ROV.Op->ImplicitReg);
- }
-}
-
-size_t randomBit(const llvm::BitVector &Vector) {
- assert(Vector.any());
- auto Itr = Vector.set_bits_begin();
- for (size_t I = randomIndex(Vector.count()); I != 0; --I)
- ++Itr;
- return *Itr;
-}
-
-void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations,
- InstructionInstance &DefII, InstructionInstance &UseII) {
- assert(!AliasingConfigurations.empty());
- assert(!AliasingConfigurations.hasImplicitAliasing());
- const auto &RandomConf = randomElement(AliasingConfigurations.Configurations);
- setRegisterOperandValue(randomElement(RandomConf.Defs), DefII);
- setRegisterOperandValue(randomElement(RandomConf.Uses), UseII);
-}
-
void DumpMCOperand(const llvm::MCRegisterInfo &MCRegisterInfo,
const llvm::MCOperand &Op, llvm::raw_ostream &OS) {
if (!Op.isValid())
@@ -305,3 +355,4 @@ void DumpMCInst(const llvm::MCRegisterInfo &MCRegisterInfo,
}
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/MCInstrDescView.h b/tools/llvm-exegesis/lib/MCInstrDescView.h
index c36582ac72d8..58efd2a4e41c 100644
--- a/tools/llvm-exegesis/lib/MCInstrDescView.h
+++ b/tools/llvm-exegesis/lib/MCInstrDescView.h
@@ -20,6 +20,7 @@
#define LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H
#include <random>
+#include <unordered_map>
#include "RegisterAliasing.h"
#include "llvm/ADT/ArrayRef.h"
@@ -28,24 +29,31 @@
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/MC/MCInstrInfo.h"
+namespace llvm {
namespace exegesis {
-struct Operand; // forward declaration.
-
// A variable represents the value associated to an Operand or a set of Operands
// if they are tied together.
struct Variable {
+ // Returns the index of this Variable inside Instruction's Variable.
+ unsigned getIndex() const;
+
+ // Returns the index of the Operand linked to this Variable.
+ unsigned getPrimaryOperandIndex() const;
+
+ // Returns whether this Variable has more than one Operand linked to it.
+ bool hasTiedOperands() const;
+
// The indices of the operands tied to this Variable.
llvm::SmallVector<unsigned, 2> TiedOperands;
- llvm::MCOperand AssignedValue;
+
// The index of this Variable in Instruction.Variables and its associated
- // Value in InstructionInstance.VariableValues.
- unsigned Index = -1;
+ // Value in InstructionBuilder.VariableValues.
+ int Index = -1;
};
// MCOperandInfo can only represents Explicit operands. This object gives a
// uniform view of Implicit and Explicit Operands.
-//
// - Index: can be used to refer to MCInstrDesc::operands for Explicit operands.
// - Tracker: is set for Register Operands and is used to keep track of possible
// registers and the registers reachable from them (aliasing registers).
@@ -56,9 +64,26 @@ struct Variable {
// - VariableIndex: the index of the Variable holding the value for this Operand
// or -1 if this operand is implicit.
struct Operand {
- unsigned Index = 0;
+ bool isExplicit() const;
+ bool isImplicit() const;
+ bool isImplicitReg() const;
+ bool isDef() const;
+ bool isUse() const;
+ bool isReg() const;
+ bool isTied() const;
+ bool isVariable() const;
+ bool isMemory() const;
+ bool isImmediate() const;
+ unsigned getIndex() const;
+ unsigned getTiedToIndex() const;
+ unsigned getVariableIndex() const;
+ unsigned getImplicitReg() const;
+ const RegisterAliasingTracker &getRegisterAliasing() const;
+ const llvm::MCOperandInfo &getExplicitOperandInfo() const;
+
+ // Please use the accessors above and not the following fields.
+ int Index = -1;
bool IsDef = false;
- bool IsExplicit = false;
const RegisterAliasingTracker *Tracker = nullptr; // Set for Register Op.
const llvm::MCOperandInfo *Info = nullptr; // Set for Explicit Op.
int TiedToIndex = -1; // Set for Reg&Explicit Op.
@@ -69,63 +94,72 @@ struct Operand {
// A view over an MCInstrDesc offering a convenient interface to compute
// Register aliasing.
struct Instruction {
- Instruction(const llvm::MCInstrDesc &MCInstrDesc,
- const RegisterAliasingTrackerCache &ATC);
+ Instruction(const llvm::MCInstrInfo &InstrInfo,
+ const RegisterAliasingTrackerCache &RATC, unsigned Opcode);
- const llvm::MCInstrDesc *Description; // Never nullptr.
- llvm::SmallVector<Operand, 8> Operands;
- llvm::SmallVector<Variable, 4> Variables;
- llvm::BitVector DefRegisters; // The union of the aliased def registers.
- llvm::BitVector UseRegisters; // The union of the aliased use registers.
-};
+ // Returns the Operand linked to this Variable.
+ // In case the Variable is tied, the primary (i.e. Def) Operand is returned.
+ const Operand &getPrimaryOperand(const Variable &Var) const;
-// An instance of an Instruction holding values for each of its Variables.
-struct InstructionInstance {
- InstructionInstance(const Instruction &Instr);
+ // Whether this instruction is self aliasing through its tied registers.
+ // Repeating this instruction is guaranteed to executes sequentially.
+ bool hasTiedRegisters() const;
- // No copy.
- InstructionInstance(const InstructionInstance &) = delete;
- InstructionInstance &operator=(const InstructionInstance &) = delete;
+ // Whether this instruction is self aliasing through its implicit registers.
+ // Repeating this instruction is guaranteed to executes sequentially.
+ bool hasAliasingImplicitRegisters() const;
- // Moving is OK.
- InstructionInstance(InstructionInstance &&);
- InstructionInstance &operator=(InstructionInstance &&);
+ // Whether this instruction is self aliasing through some registers.
+ // Repeating this instruction may execute sequentially by picking aliasing
+ // Use and Def registers. It may also execute in parallel by picking non
+ // aliasing Use and Def registers.
+ bool hasAliasingRegisters() const;
- unsigned getOpcode() const;
- llvm::MCOperand &getValueFor(const Variable &Var);
- const llvm::MCOperand &getValueFor(const Variable &Var) const;
- llvm::MCOperand &getValueFor(const Operand &Op);
- const llvm::MCOperand &getValueFor(const Operand &Op) const;
- bool hasImmediateVariables() const;
+ // Whether this instruction's implicit registers alias with OtherInstr's
+ // implicit registers.
+ bool hasAliasingImplicitRegistersThrough(const Instruction &OtherInstr) const;
- // Assigns a Random Value to all Variables that are still Invalid.
- void randomizeUnsetVariables();
+ // Whether this instruction's registers alias with OtherInstr's registers.
+ bool hasAliasingRegistersThrough(const Instruction &OtherInstr) const;
- // Returns the instance as an llvm::MCInst. The InstructionInstance must be
- // fully allocated (no invalid variables).
- llvm::MCInst build() const;
+ // Returns whether this instruction has Memory Operands.
+ // Repeating this instruction executes sequentially with an instruction that
+ // reads or write the same memory region.
+ bool hasMemoryOperands() const;
- Instruction Instr;
- llvm::SmallVector<llvm::MCOperand, 4> VariableValues;
-};
+ // Returns whether this instruction as at least one use or one def.
+ // Repeating this instruction may execute sequentially by adding an
+ // instruction that aliases one of these.
+ bool hasOneUseOrOneDef() const;
-// A prototype is a set of InstructionInstances with an explanation of how
-// it's been built. The prototype can then be randomized to exercice several
-// immediate values. It is also used to gather the used registers and define
-// their initial values.
-struct SnippetPrototype {
- SnippetPrototype() = default;
+ // Convenient function to help with debugging.
+ void dump(const llvm::MCRegisterInfo &RegInfo,
+ llvm::raw_ostream &Stream) const;
- // No copy.
- SnippetPrototype(const SnippetPrototype &) = delete;
- SnippetPrototype &operator=(const SnippetPrototype &) = delete;
+ const llvm::MCInstrDesc *Description; // Never nullptr.
+ llvm::StringRef Name; // The name of this instruction.
+ llvm::SmallVector<Operand, 8> Operands;
+ llvm::SmallVector<Variable, 4> Variables;
+ llvm::BitVector ImplDefRegs; // The set of aliased implicit def registers.
+ llvm::BitVector ImplUseRegs; // The set of aliased implicit use registers.
+ llvm::BitVector AllDefRegs; // The set of all aliased def registers.
+ llvm::BitVector AllUseRegs; // The set of all aliased use registers.
+};
- // Moving is OK.
- SnippetPrototype(SnippetPrototype &&);
- SnippetPrototype &operator=(SnippetPrototype &&);
+// Instructions are expensive to instantiate. This class provides a cache of
+// Instructions with lazy construction.
+struct InstructionsCache {
+ InstructionsCache(const llvm::MCInstrInfo &InstrInfo,
+ const RegisterAliasingTrackerCache &RATC);
- std::string Explanation;
- std::vector<InstructionInstance> Snippet;
+ // Returns the Instruction object corresponding to this Opcode.
+ const Instruction &getInstr(unsigned Opcode) const;
+
+private:
+ const llvm::MCInstrInfo &InstrInfo;
+ const RegisterAliasingTrackerCache &RATC;
+ mutable std::unordered_map<unsigned, std::unique_ptr<Instruction>>
+ Instructions;
};
// Represents the assignment of a Register to an Operand.
@@ -163,27 +197,10 @@ struct AliasingConfigurations {
bool empty() const; // True if no aliasing configuration is found.
bool hasImplicitAliasing() const;
- void setExplicitAliasing() const;
- const Instruction &DefInstruction;
- const Instruction &UseInstruction;
llvm::SmallVector<AliasingRegisterOperands, 32> Configurations;
};
-// A global Random Number Generator to randomize configurations.
-// FIXME: Move random number generation into an object and make it seedable for
-// unit tests.
-std::mt19937 &randomGenerator();
-
-// Picks a random bit among the bits set in Vector and returns its index.
-// Precondition: Vector must have at least one bit set.
-size_t randomBit(const llvm::BitVector &Vector);
-
-// Picks a random configuration, then selects a random def and a random use from
-// it and finally set the selected values in the provided InstructionInstances.
-void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations,
- InstructionInstance &DefII, InstructionInstance &UseII);
-
// Writes MCInst to OS.
// This is not assembly but the internal LLVM's name for instructions and
// registers.
@@ -192,5 +209,6 @@ void DumpMCInst(const llvm::MCRegisterInfo &MCRegisterInfo,
const llvm::MCInst &MCInst, llvm::raw_ostream &OS);
} // namespace exegesis
+} // namespace llvm
#endif // LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H
diff --git a/tools/llvm-exegesis/lib/PerfHelper.cpp b/tools/llvm-exegesis/lib/PerfHelper.cpp
index c145ea8404b4..c1c242ca88fa 100644
--- a/tools/llvm-exegesis/lib/PerfHelper.cpp
+++ b/tools/llvm-exegesis/lib/PerfHelper.cpp
@@ -17,6 +17,7 @@
#endif
#include <cassert>
+namespace llvm {
namespace exegesis {
namespace pfm {
@@ -136,3 +137,4 @@ int64_t Counter::read() const { return 42; }
} // namespace pfm
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/PerfHelper.h b/tools/llvm-exegesis/lib/PerfHelper.h
index 8c3f13e6c5cd..2d0810846606 100644
--- a/tools/llvm-exegesis/lib/PerfHelper.h
+++ b/tools/llvm-exegesis/lib/PerfHelper.h
@@ -23,6 +23,7 @@
struct perf_event_attr;
+namespace llvm {
namespace exegesis {
namespace pfm {
@@ -102,5 +103,6 @@ void Measure(
} // namespace pfm
} // namespace exegesis
+} // namespace llvm
#endif // LLVM_TOOLS_LLVM_EXEGESIS_PERFHELPER_H
diff --git a/tools/llvm-exegesis/lib/PowerPC/CMakeLists.txt b/tools/llvm-exegesis/lib/PowerPC/CMakeLists.txt
new file mode 100644
index 000000000000..89e33437952e
--- /dev/null
+++ b/tools/llvm-exegesis/lib/PowerPC/CMakeLists.txt
@@ -0,0 +1,18 @@
+include_directories(
+ ${LLVM_MAIN_SRC_DIR}/lib/Target/PowerPC
+ ${LLVM_BINARY_DIR}/lib/Target/PowerPC
+ )
+
+add_library(LLVMExegesisPowerPC
+ STATIC
+ Target.cpp
+ )
+
+llvm_update_compile_flags(LLVMExegesisPowerPC)
+llvm_map_components_to_libnames(libs
+ PowerPC
+ Exegesis
+ )
+
+target_link_libraries(LLVMExegesisPowerPC ${libs})
+set_target_properties(LLVMExegesisPowerPC PROPERTIES FOLDER "Libraries")
diff --git a/tools/llvm-exegesis/lib/PowerPC/LLVMBuild.txt b/tools/llvm-exegesis/lib/PowerPC/LLVMBuild.txt
new file mode 100644
index 000000000000..c535562065eb
--- /dev/null
+++ b/tools/llvm-exegesis/lib/PowerPC/LLVMBuild.txt
@@ -0,0 +1,22 @@
+;===- ./tools/llvm-exegesis/lib/PowerPC/LLVMBuild.txt ---------------*- Conf -*--===;
+;
+; The LLVM Compiler Infrastructure
+;
+; This file is distributed under the University of Illinois Open Source
+; License. See LICENSE.TXT for details.
+;
+;===------------------------------------------------------------------------===;
+;
+; This is an LLVMBuild description file for the components in this subdirectory.
+;
+; For more information on the LLVMBuild system, please see:
+;
+; http://llvm.org/docs/LLVMBuild.html
+;
+;===------------------------------------------------------------------------===;
+
+[component_0]
+type = Library
+name = ExegesisPowerPC
+parent = Libraries
+required_libraries = PowerPC
diff --git a/tools/llvm-exegesis/lib/PowerPC/Target.cpp b/tools/llvm-exegesis/lib/PowerPC/Target.cpp
new file mode 100644
index 000000000000..b4bdb5dc4adc
--- /dev/null
+++ b/tools/llvm-exegesis/lib/PowerPC/Target.cpp
@@ -0,0 +1,76 @@
+//===-- Target.cpp ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+// The PowerPC ExegesisTarget.
+//===----------------------------------------------------------------------===//
+#include "../Target.h"
+#include "../Latency.h"
+#include "PPC.h"
+#include "PPCRegisterInfo.h"
+
+namespace llvm {
+namespace exegesis {
+
+#include "PPCGenExegesis.inc"
+
+namespace {
+class ExegesisPowerPCTarget : public ExegesisTarget {
+public:
+ ExegesisPowerPCTarget() : ExegesisTarget(PPCCpuPfmCounters) {}
+
+private:
+ std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI,
+ unsigned Reg,
+ const llvm::APInt &Value) const override;
+ bool matchesArch(llvm::Triple::ArchType Arch) const override {
+ return Arch == llvm::Triple::ppc64le;
+ }
+};
+} // end anonymous namespace
+
+static unsigned getLoadImmediateOpcode(unsigned RegBitWidth) {
+ switch (RegBitWidth) {
+ case 32:
+ return llvm::PPC::LI;
+ case 64:
+ return llvm::PPC::LI8;
+ }
+ llvm_unreachable("Invalid Value Width");
+}
+
+// Generates instruction to load an immediate value into a register.
+static llvm::MCInst loadImmediate(unsigned Reg, unsigned RegBitWidth,
+ const llvm::APInt &Value) {
+ if (Value.getBitWidth() > RegBitWidth)
+ llvm_unreachable("Value must fit in the Register");
+ return llvm::MCInstBuilder(getLoadImmediateOpcode(RegBitWidth))
+ .addReg(Reg)
+ .addImm(Value.getZExtValue());
+}
+
+std::vector<llvm::MCInst>
+ExegesisPowerPCTarget::setRegTo(const llvm::MCSubtargetInfo &STI, unsigned Reg,
+ const llvm::APInt &Value) const {
+ if (llvm::PPC::GPRCRegClass.contains(Reg))
+ return {loadImmediate(Reg, 32, Value)};
+ if (llvm::PPC::G8RCRegClass.contains(Reg))
+ return {loadImmediate(Reg, 64, Value)};
+ llvm::errs() << "setRegTo is not implemented, results will be unreliable\n";
+ return {};
+}
+
+static ExegesisTarget *getTheExegesisPowerPCTarget() {
+ static ExegesisPowerPCTarget Target;
+ return &Target;
+}
+
+void InitializePowerPCExegesisTarget() {
+ ExegesisTarget::registerTarget(getTheExegesisPowerPCTarget());
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/RegisterAliasing.cpp b/tools/llvm-exegesis/lib/RegisterAliasing.cpp
index 039f78db985f..54041ca30aa0 100644
--- a/tools/llvm-exegesis/lib/RegisterAliasing.cpp
+++ b/tools/llvm-exegesis/lib/RegisterAliasing.cpp
@@ -9,6 +9,7 @@
#include "RegisterAliasing.h"
+namespace llvm {
namespace exegesis {
llvm::BitVector getAliasedBits(const llvm::MCRegisterInfo &RegInfo,
@@ -81,3 +82,4 @@ RegisterAliasingTrackerCache::getRegisterClass(unsigned RegClassIndex) const {
}
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/RegisterAliasing.h b/tools/llvm-exegesis/lib/RegisterAliasing.h
index 0b0360ef9ff4..94a2eb07f495 100644
--- a/tools/llvm-exegesis/lib/RegisterAliasing.h
+++ b/tools/llvm-exegesis/lib/RegisterAliasing.h
@@ -22,6 +22,7 @@
#include "llvm/ADT/PackedVector.h"
#include "llvm/MC/MCRegisterInfo.h"
+namespace llvm {
namespace exegesis {
// Returns the registers that are aliased by the ones set in SourceBits.
@@ -62,6 +63,7 @@ struct RegisterAliasingTracker {
private:
RegisterAliasingTracker(const llvm::MCRegisterInfo &RegInfo);
+ RegisterAliasingTracker(const RegisterAliasingTracker &) = delete;
void FillOriginAndAliasedBits(const llvm::MCRegisterInfo &RegInfo,
const llvm::BitVector &OriginalBits);
@@ -103,5 +105,6 @@ private:
};
} // namespace exegesis
+} // namespace llvm
#endif // LLVM_TOOLS_LLVM_EXEGESIS_ALIASINGTRACKER_H
diff --git a/tools/llvm-exegesis/lib/RegisterValue.cpp b/tools/llvm-exegesis/lib/RegisterValue.cpp
new file mode 100644
index 000000000000..2bf996cead48
--- /dev/null
+++ b/tools/llvm-exegesis/lib/RegisterValue.cpp
@@ -0,0 +1,51 @@
+//===-- RegisterValue.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterValue.h"
+#include "llvm/ADT/APFloat.h"
+
+namespace llvm {
+namespace exegesis {
+
+static llvm::APFloat getFloatValue(const llvm::fltSemantics &FltSemantics,
+ PredefinedValues Value) {
+ switch (Value) {
+ case PredefinedValues::POS_ZERO:
+ return llvm::APFloat::getZero(FltSemantics);
+ case PredefinedValues::NEG_ZERO:
+ return llvm::APFloat::getZero(FltSemantics, true);
+ case PredefinedValues::ONE:
+ return llvm::APFloat(FltSemantics, "1");
+ case PredefinedValues::TWO:
+ return llvm::APFloat(FltSemantics, "2");
+ case PredefinedValues::INF:
+ return llvm::APFloat::getInf(FltSemantics);
+ case PredefinedValues::QNAN:
+ return llvm::APFloat::getQNaN(FltSemantics);
+ case PredefinedValues::SMALLEST_NORM:
+ return llvm::APFloat::getSmallestNormalized(FltSemantics);
+ case PredefinedValues::LARGEST:
+ return llvm::APFloat::getLargest(FltSemantics);
+ case PredefinedValues::ULP:
+ return llvm::APFloat::getSmallest(FltSemantics);
+ case PredefinedValues::ONE_PLUS_ULP:
+ auto Output = getFloatValue(FltSemantics, PredefinedValues::ONE);
+ Output.next(false);
+ return Output;
+ }
+ llvm_unreachable("Unhandled exegesis::PredefinedValues");
+}
+
+llvm::APInt bitcastFloatValue(const llvm::fltSemantics &FltSemantics,
+ PredefinedValues Value) {
+ return getFloatValue(FltSemantics, Value).bitcastToAPInt();
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/RegisterValue.h b/tools/llvm-exegesis/lib/RegisterValue.h
new file mode 100644
index 000000000000..689e354e2416
--- /dev/null
+++ b/tools/llvm-exegesis/lib/RegisterValue.h
@@ -0,0 +1,53 @@
+//===-- RegisterValue.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+///
+/// Defines a Target independent value for a Register. This is useful to explore
+/// the influence of the instruction input values on its execution time.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_REGISTERVALUE_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_REGISTERVALUE_H
+
+#include <llvm/ADT/APFloat.h>
+#include <llvm/ADT/APInt.h>
+
+namespace llvm {
+namespace exegesis {
+
+// A simple object storing the value for a particular register.
+struct RegisterValue {
+ static RegisterValue zero(unsigned Reg) { return {Reg, llvm::APInt()}; }
+ unsigned Register;
+ llvm::APInt Value;
+};
+
+enum class PredefinedValues {
+ POS_ZERO, // Positive zero
+ NEG_ZERO, // Negative zero
+ ONE, // 1.0
+ TWO, // 2.0
+ INF, // Infinity
+ QNAN, // Quiet NaN
+ ULP, // One Unit in the last place
+ SMALLEST = ULP, // The minimum subnormal number
+ SMALLEST_NORM, // The minimum normal number
+ LARGEST, // The maximum normal number
+ ONE_PLUS_ULP, // The value just after 1.0
+};
+
+llvm::APInt bitcastFloatValue(const llvm::fltSemantics &FltSemantics,
+ PredefinedValues Value);
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_REGISTERVALUE_H
diff --git a/tools/llvm-exegesis/lib/SnippetGenerator.cpp b/tools/llvm-exegesis/lib/SnippetGenerator.cpp
new file mode 100644
index 000000000000..88ba315548d3
--- /dev/null
+++ b/tools/llvm-exegesis/lib/SnippetGenerator.cpp
@@ -0,0 +1,226 @@
+//===-- SnippetGenerator.cpp ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <array>
+#include <string>
+
+#include "Assembler.h"
+#include "MCInstrDescView.h"
+#include "SnippetGenerator.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Program.h"
+
+namespace llvm {
+namespace exegesis {
+
+std::vector<CodeTemplate> getSingleton(CodeTemplate &&CT) {
+ std::vector<CodeTemplate> Result;
+ Result.push_back(std::move(CT));
+ return Result;
+}
+
+SnippetGeneratorFailure::SnippetGeneratorFailure(const llvm::Twine &S)
+ : llvm::StringError(S, llvm::inconvertibleErrorCode()) {}
+
+SnippetGenerator::SnippetGenerator(const LLVMState &State) : State(State) {}
+
+SnippetGenerator::~SnippetGenerator() = default;
+
+llvm::Expected<std::vector<BenchmarkCode>>
+SnippetGenerator::generateConfigurations(const Instruction &Instr) const {
+ if (auto E = generateCodeTemplates(Instr)) {
+ const auto &RATC = State.getRATC();
+ std::vector<BenchmarkCode> Output;
+ for (CodeTemplate &CT : E.get()) {
+ const llvm::BitVector &ForbiddenRegs =
+ CT.ScratchSpacePointerInReg
+ ? RATC.getRegister(CT.ScratchSpacePointerInReg).aliasedBits()
+ : RATC.emptyRegisters();
+ // TODO: Generate as many BenchmarkCode as needed.
+ {
+ BenchmarkCode BC;
+ BC.Info = CT.Info;
+ for (InstructionTemplate &IT : CT.Instructions) {
+ randomizeUnsetVariables(ForbiddenRegs, IT);
+ BC.Instructions.push_back(IT.build());
+ }
+ if (CT.ScratchSpacePointerInReg)
+ BC.LiveIns.push_back(CT.ScratchSpacePointerInReg);
+ BC.RegisterInitialValues =
+ computeRegisterInitialValues(CT.Instructions);
+ Output.push_back(std::move(BC));
+ }
+ }
+ return Output;
+ } else
+ return E.takeError();
+}
+
+std::vector<RegisterValue> SnippetGenerator::computeRegisterInitialValues(
+ const std::vector<InstructionTemplate> &Instructions) const {
+ // Collect all register uses and create an assignment for each of them.
+ // Ignore memory operands which are handled separately.
+ // Loop invariant: DefinedRegs[i] is true iif it has been set at least once
+ // before the current instruction.
+ llvm::BitVector DefinedRegs = State.getRATC().emptyRegisters();
+ std::vector<RegisterValue> RIV;
+ for (const InstructionTemplate &IT : Instructions) {
+ // Returns the register that this Operand sets or uses, or 0 if this is not
+ // a register.
+ const auto GetOpReg = [&IT](const Operand &Op) -> unsigned {
+ if (Op.isMemory())
+ return 0;
+ if (Op.isImplicitReg())
+ return Op.getImplicitReg();
+ if (Op.isExplicit() && IT.getValueFor(Op).isReg())
+ return IT.getValueFor(Op).getReg();
+ return 0;
+ };
+ // Collect used registers that have never been def'ed.
+ for (const Operand &Op : IT.Instr.Operands) {
+ if (Op.isUse()) {
+ const unsigned Reg = GetOpReg(Op);
+ if (Reg > 0 && !DefinedRegs.test(Reg)) {
+ RIV.push_back(RegisterValue::zero(Reg));
+ DefinedRegs.set(Reg);
+ }
+ }
+ }
+ // Mark defs as having been def'ed.
+ for (const Operand &Op : IT.Instr.Operands) {
+ if (Op.isDef()) {
+ const unsigned Reg = GetOpReg(Op);
+ if (Reg > 0)
+ DefinedRegs.set(Reg);
+ }
+ }
+ }
+ return RIV;
+}
+
+llvm::Expected<std::vector<CodeTemplate>>
+generateSelfAliasingCodeTemplates(const Instruction &Instr) {
+ const AliasingConfigurations SelfAliasing(Instr, Instr);
+ if (SelfAliasing.empty())
+ return llvm::make_error<SnippetGeneratorFailure>("empty self aliasing");
+ std::vector<CodeTemplate> Result;
+ Result.emplace_back();
+ CodeTemplate &CT = Result.back();
+ InstructionTemplate IT(Instr);
+ if (SelfAliasing.hasImplicitAliasing()) {
+ CT.Info = "implicit Self cycles, picking random values.";
+ } else {
+ CT.Info = "explicit self cycles, selecting one aliasing Conf.";
+ // This is a self aliasing instruction so defs and uses are from the same
+ // instance, hence twice IT in the following call.
+ setRandomAliasing(SelfAliasing, IT, IT);
+ }
+ CT.Instructions.push_back(std::move(IT));
+ return std::move(Result);
+}
+
+llvm::Expected<std::vector<CodeTemplate>>
+generateUnconstrainedCodeTemplates(const Instruction &Instr,
+ llvm::StringRef Msg) {
+ std::vector<CodeTemplate> Result;
+ Result.emplace_back();
+ CodeTemplate &CT = Result.back();
+ CT.Info = llvm::formatv("{0}, repeating an unconstrained assignment", Msg);
+ CT.Instructions.emplace_back(Instr);
+ return std::move(Result);
+}
+
+std::mt19937 &randomGenerator() {
+ static std::random_device RandomDevice;
+ static std::mt19937 RandomGenerator(RandomDevice());
+ return RandomGenerator;
+}
+
+static size_t randomIndex(size_t Size) {
+ assert(Size > 0);
+ std::uniform_int_distribution<> Distribution(0, Size - 1);
+ return Distribution(randomGenerator());
+}
+
+template <typename C>
+static auto randomElement(const C &Container) -> decltype(Container[0]) {
+ return Container[randomIndex(Container.size())];
+}
+
+static void randomize(const Instruction &Instr, const Variable &Var,
+ llvm::MCOperand &AssignedValue,
+ const llvm::BitVector &ForbiddenRegs) {
+ const Operand &Op = Instr.getPrimaryOperand(Var);
+ switch (Op.getExplicitOperandInfo().OperandType) {
+ case llvm::MCOI::OperandType::OPERAND_IMMEDIATE:
+ // FIXME: explore immediate values too.
+ AssignedValue = llvm::MCOperand::createImm(1);
+ break;
+ case llvm::MCOI::OperandType::OPERAND_REGISTER: {
+ assert(Op.isReg());
+ auto AllowedRegs = Op.getRegisterAliasing().sourceBits();
+ assert(AllowedRegs.size() == ForbiddenRegs.size());
+ for (auto I : ForbiddenRegs.set_bits())
+ AllowedRegs.reset(I);
+ AssignedValue = llvm::MCOperand::createReg(randomBit(AllowedRegs));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void setRegisterOperandValue(const RegisterOperandAssignment &ROV,
+ InstructionTemplate &IB) {
+ assert(ROV.Op);
+ if (ROV.Op->isExplicit()) {
+ auto &AssignedValue = IB.getValueFor(*ROV.Op);
+ if (AssignedValue.isValid()) {
+ assert(AssignedValue.isReg() && AssignedValue.getReg() == ROV.Reg);
+ return;
+ }
+ AssignedValue = llvm::MCOperand::createReg(ROV.Reg);
+ } else {
+ assert(ROV.Op->isImplicitReg());
+ assert(ROV.Reg == ROV.Op->getImplicitReg());
+ }
+}
+
+size_t randomBit(const llvm::BitVector &Vector) {
+ assert(Vector.any());
+ auto Itr = Vector.set_bits_begin();
+ for (size_t I = randomIndex(Vector.count()); I != 0; --I)
+ ++Itr;
+ return *Itr;
+}
+
+void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations,
+ InstructionTemplate &DefIB, InstructionTemplate &UseIB) {
+ assert(!AliasingConfigurations.empty());
+ assert(!AliasingConfigurations.hasImplicitAliasing());
+ const auto &RandomConf = randomElement(AliasingConfigurations.Configurations);
+ setRegisterOperandValue(randomElement(RandomConf.Defs), DefIB);
+ setRegisterOperandValue(randomElement(RandomConf.Uses), UseIB);
+}
+
+void randomizeUnsetVariables(const llvm::BitVector &ForbiddenRegs,
+ InstructionTemplate &IT) {
+ for (const Variable &Var : IT.Instr.Variables) {
+ llvm::MCOperand &AssignedValue = IT.getValueFor(Var);
+ if (!AssignedValue.isValid())
+ randomize(IT.Instr, Var, AssignedValue, ForbiddenRegs);
+ }
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/SnippetGenerator.h b/tools/llvm-exegesis/lib/SnippetGenerator.h
new file mode 100644
index 000000000000..967b273182b7
--- /dev/null
+++ b/tools/llvm-exegesis/lib/SnippetGenerator.h
@@ -0,0 +1,98 @@
+//===-- SnippetGenerator.h --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines the abstract SnippetGenerator class for generating code that allows
+/// measuring a certain property of instructions (e.g. latency).
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETGENERATOR_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETGENERATOR_H
+
+#include "Assembler.h"
+#include "BenchmarkCode.h"
+#include "CodeTemplate.h"
+#include "LlvmState.h"
+#include "MCInstrDescView.h"
+#include "RegisterAliasing.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/Support/Error.h"
+#include <cstdlib>
+#include <memory>
+#include <vector>
+
+namespace llvm {
+namespace exegesis {
+
+std::vector<CodeTemplate> getSingleton(CodeTemplate &&CT);
+
+// Generates code templates that has a self-dependency.
+llvm::Expected<std::vector<CodeTemplate>>
+generateSelfAliasingCodeTemplates(const Instruction &Instr);
+
+// Generates code templates without assignment constraints.
+llvm::Expected<std::vector<CodeTemplate>>
+generateUnconstrainedCodeTemplates(const Instruction &Instr,
+ llvm::StringRef Msg);
+
+// A class representing failures that happened during Benchmark, they are used
+// to report informations to the user.
+class SnippetGeneratorFailure : public llvm::StringError {
+public:
+ SnippetGeneratorFailure(const llvm::Twine &S);
+};
+
+// Common code for all benchmark modes.
+class SnippetGenerator {
+public:
+ explicit SnippetGenerator(const LLVMState &State);
+
+ virtual ~SnippetGenerator();
+
+ // Calls generateCodeTemplate and expands it into one or more BenchmarkCode.
+ llvm::Expected<std::vector<BenchmarkCode>>
+ generateConfigurations(const Instruction &Instr) const;
+
+ // Given a snippet, computes which registers the setup code needs to define.
+ std::vector<RegisterValue> computeRegisterInitialValues(
+ const std::vector<InstructionTemplate> &Snippet) const;
+
+protected:
+ const LLVMState &State;
+
+private:
+ // API to be implemented by subclasses.
+ virtual llvm::Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(const Instruction &Instr) const = 0;
+};
+
+// A global Random Number Generator to randomize configurations.
+// FIXME: Move random number generation into an object and make it seedable for
+// unit tests.
+std::mt19937 &randomGenerator();
+
+// Picks a random bit among the bits set in Vector and returns its index.
+// Precondition: Vector must have at least one bit set.
+size_t randomBit(const llvm::BitVector &Vector);
+
+// Picks a random configuration, then selects a random def and a random use from
+// it and finally set the selected values in the provided InstructionInstances.
+void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations,
+ InstructionTemplate &DefIB, InstructionTemplate &UseIB);
+
+// Assigns a Random Value to all Variables in IT that are still Invalid.
+// Do not use any of the registers in `ForbiddenRegs`.
+void randomizeUnsetVariables(const llvm::BitVector &ForbiddenRegs,
+ InstructionTemplate &IT);
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETGENERATOR_H
diff --git a/tools/llvm-exegesis/lib/Target.cpp b/tools/llvm-exegesis/lib/Target.cpp
index 44156c815dbe..2c89d27b4ab6 100644
--- a/tools/llvm-exegesis/lib/Target.cpp
+++ b/tools/llvm-exegesis/lib/Target.cpp
@@ -11,6 +11,7 @@
#include "Latency.h"
#include "Uops.h"
+namespace llvm {
namespace exegesis {
ExegesisTarget::~ExegesisTarget() {} // anchor.
@@ -30,13 +31,26 @@ void ExegesisTarget::registerTarget(ExegesisTarget *Target) {
FirstTarget = Target;
return;
}
- assert(Target->Next == nullptr && "target has already been registered");
if (Target->Next != nullptr)
- return;
+ return; // Already registered.
Target->Next = FirstTarget;
FirstTarget = Target;
}
+std::unique_ptr<SnippetGenerator>
+ExegesisTarget::createSnippetGenerator(InstructionBenchmark::ModeE Mode,
+ const LLVMState &State) const {
+ switch (Mode) {
+ case InstructionBenchmark::Unknown:
+ return nullptr;
+ case InstructionBenchmark::Latency:
+ return createLatencySnippetGenerator(State);
+ case InstructionBenchmark::Uops:
+ return createUopsSnippetGenerator(State);
+ }
+ return nullptr;
+}
+
std::unique_ptr<BenchmarkRunner>
ExegesisTarget::createBenchmarkRunner(InstructionBenchmark::ModeE Mode,
const LLVMState &State) const {
@@ -51,6 +65,16 @@ ExegesisTarget::createBenchmarkRunner(InstructionBenchmark::ModeE Mode,
return nullptr;
}
+std::unique_ptr<SnippetGenerator>
+ExegesisTarget::createLatencySnippetGenerator(const LLVMState &State) const {
+ return llvm::make_unique<LatencySnippetGenerator>(State);
+}
+
+std::unique_ptr<SnippetGenerator>
+ExegesisTarget::createUopsSnippetGenerator(const LLVMState &State) const {
+ return llvm::make_unique<UopsSnippetGenerator>(State);
+}
+
std::unique_ptr<BenchmarkRunner>
ExegesisTarget::createLatencyBenchmarkRunner(const LLVMState &State) const {
return llvm::make_unique<LatencyBenchmarkRunner>(State);
@@ -61,11 +85,51 @@ ExegesisTarget::createUopsBenchmarkRunner(const LLVMState &State) const {
return llvm::make_unique<UopsBenchmarkRunner>(State);
}
+static_assert(std::is_pod<PfmCountersInfo>::value,
+ "We shouldn't have dynamic initialization here");
+const PfmCountersInfo PfmCountersInfo::Default = {nullptr, nullptr, nullptr,
+ 0u};
+
+const PfmCountersInfo &
+ExegesisTarget::getPfmCounters(llvm::StringRef CpuName) const {
+ assert(std::is_sorted(
+ CpuPfmCounters.begin(), CpuPfmCounters.end(),
+ [](const CpuAndPfmCounters &LHS, const CpuAndPfmCounters &RHS) {
+ return strcmp(LHS.CpuName, RHS.CpuName) < 0;
+ }) &&
+ "CpuPfmCounters table is not sorted");
+
+ // Find entry
+ auto Found =
+ std::lower_bound(CpuPfmCounters.begin(), CpuPfmCounters.end(), CpuName);
+ if (Found == CpuPfmCounters.end() ||
+ llvm::StringRef(Found->CpuName) != CpuName) {
+ // Use the default.
+ if (CpuPfmCounters.begin() != CpuPfmCounters.end() &&
+ CpuPfmCounters.begin()->CpuName[0] == '\0') {
+ Found = CpuPfmCounters.begin(); // The target specifies a default.
+ } else {
+ return PfmCountersInfo::Default; // No default for the target.
+ }
+ }
+ assert(Found->PCI && "Missing counters");
+ return *Found->PCI;
+}
+
namespace {
// Default implementation.
class ExegesisDefaultTarget : public ExegesisTarget {
+public:
+ ExegesisDefaultTarget() : ExegesisTarget({}) {}
+
private:
+ std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI,
+ unsigned Reg,
+ const llvm::APInt &Value) const override {
+ llvm_unreachable("Not yet implemented");
+ }
+
bool matchesArch(llvm::Triple::ArchType Arch) const override {
llvm_unreachable("never called");
return false;
@@ -80,3 +144,4 @@ const ExegesisTarget &ExegesisTarget::getDefault() {
}
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/Target.h b/tools/llvm-exegesis/lib/Target.h
index 4f1f2869b749..b0f0e9961736 100644
--- a/tools/llvm-exegesis/lib/Target.h
+++ b/tools/llvm-exegesis/lib/Target.h
@@ -20,25 +20,88 @@
#include "BenchmarkResult.h"
#include "BenchmarkRunner.h"
#include "LlvmState.h"
+#include "SnippetGenerator.h"
#include "llvm/ADT/Triple.h"
#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/IR/CallingConv.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCRegisterInfo.h"
+namespace llvm {
namespace exegesis {
+struct PfmCountersInfo {
+ // An optional name of a performance counter that can be used to measure
+ // cycles.
+ const char *CycleCounter;
+
+ // An optional name of a performance counter that can be used to measure
+ // uops.
+ const char *UopsCounter;
+
+ // An IssueCounter specifies how to measure uops issued to specific proc
+ // resources.
+ struct IssueCounter {
+ const char *Counter;
+ // The name of the ProcResource that this counter measures.
+ const char *ProcResName;
+ };
+ // An optional list of IssueCounters.
+ const IssueCounter *IssueCounters;
+ unsigned NumIssueCounters;
+
+ static const PfmCountersInfo Default;
+};
+
+struct CpuAndPfmCounters {
+ const char *CpuName;
+ const PfmCountersInfo *PCI;
+ bool operator<(llvm::StringRef S) const {
+ return llvm::StringRef(CpuName) < S;
+ }
+};
+
class ExegesisTarget {
public:
+ explicit ExegesisTarget(llvm::ArrayRef<CpuAndPfmCounters> CpuPfmCounters)
+ : CpuPfmCounters(CpuPfmCounters) {}
+
// Targets can use this to add target-specific passes in assembleToStream();
virtual void addTargetSpecificPasses(llvm::PassManagerBase &PM) const {}
// Generates code to move a constant into a the given register.
+ // Precondition: Value must fit into Reg.
virtual std::vector<llvm::MCInst>
- setRegToConstant(const llvm::MCSubtargetInfo &STI, unsigned Reg) const {
- return {};
+ setRegTo(const llvm::MCSubtargetInfo &STI, unsigned Reg,
+ const llvm::APInt &Value) const = 0;
+
+ // Returns the register pointing to scratch memory, or 0 if this target
+ // does not support memory operands. The benchmark function uses the
+ // default calling convention.
+ virtual unsigned getScratchMemoryRegister(const llvm::Triple &) const {
+ return 0;
+ }
+
+ // Fills memory operands with references to the address at [Reg] + Offset.
+ virtual void fillMemoryOperands(InstructionTemplate &IT, unsigned Reg,
+ unsigned Offset) const {
+
+ llvm_unreachable(
+ "fillMemoryOperands() requires getScratchMemoryRegister() > 0");
}
+ // Returns the maximum number of bytes a load/store instruction can access at
+ // once. This is typically the size of the largest register available on the
+ // processor. Note that this only used as a hint to generate independant
+ // load/stores to/from memory, so the exact returned value does not really
+ // matter as long as it's large enough.
+ virtual unsigned getMaxMemoryAccessSize() const { return 0; }
+
+ // Creates a snippet generator for the given mode.
+ std::unique_ptr<SnippetGenerator>
+ createSnippetGenerator(InstructionBenchmark::ModeE Mode,
+ const LLVMState &State) const;
// Creates a benchmark runner for the given mode.
std::unique_ptr<BenchmarkRunner>
createBenchmarkRunner(InstructionBenchmark::ModeE Mode,
@@ -54,19 +117,29 @@ public:
virtual ~ExegesisTarget();
+ // Returns the Pfm counters for the given CPU (or the default if no pfm
+ // counters are defined for this CPU).
+ const PfmCountersInfo &getPfmCounters(llvm::StringRef CpuName) const;
+
private:
virtual bool matchesArch(llvm::Triple::ArchType Arch) const = 0;
- // Targets can implement their own Latency/Uops benchmarks runners by
+ // Targets can implement their own snippet generators/benchmarks runners by
// implementing these.
+ std::unique_ptr<SnippetGenerator> virtual createLatencySnippetGenerator(
+ const LLVMState &State) const;
+ std::unique_ptr<SnippetGenerator> virtual createUopsSnippetGenerator(
+ const LLVMState &State) const;
std::unique_ptr<BenchmarkRunner> virtual createLatencyBenchmarkRunner(
const LLVMState &State) const;
std::unique_ptr<BenchmarkRunner> virtual createUopsBenchmarkRunner(
const LLVMState &State) const;
const ExegesisTarget *Next = nullptr;
+ const llvm::ArrayRef<CpuAndPfmCounters> CpuPfmCounters;
};
} // namespace exegesis
+} // namespace llvm
#endif // LLVM_TOOLS_LLVM_EXEGESIS_TARGET_H
diff --git a/tools/llvm-exegesis/lib/Uops.cpp b/tools/llvm-exegesis/lib/Uops.cpp
index 897f10808889..9768f4533f78 100644
--- a/tools/llvm-exegesis/lib/Uops.cpp
+++ b/tools/llvm-exegesis/lib/Uops.cpp
@@ -12,7 +12,7 @@
#include "Assembler.h"
#include "BenchmarkRunner.h"
#include "MCInstrDescView.h"
-#include "PerfHelper.h"
+#include "Target.h"
// FIXME: Load constants into registers (e.g. with fld1) to not break
// instructions like x87.
@@ -78,47 +78,14 @@
// In that case we just use a greedy register assignment and hope for the
// best.
+namespace llvm {
namespace exegesis {
-static bool hasUnknownOperand(const llvm::MCOperandInfo &OpInfo) {
- return OpInfo.OperandType == llvm::MCOI::OPERAND_UNKNOWN;
-}
-
-// FIXME: Handle memory, see PR36905.
-static bool hasMemoryOperand(const llvm::MCOperandInfo &OpInfo) {
- return OpInfo.OperandType == llvm::MCOI::OPERAND_MEMORY;
-}
-
-llvm::Error
-UopsBenchmarkRunner::isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const {
- if (llvm::any_of(MCInstrDesc.operands(), hasUnknownOperand))
- return llvm::make_error<BenchmarkFailure>(
- "Infeasible : has unknown operands");
- if (llvm::any_of(MCInstrDesc.operands(), hasMemoryOperand))
- return llvm::make_error<BenchmarkFailure>(
- "Infeasible : has memory operands");
- return llvm::Error::success();
-}
-
-// Returns whether this Variable ties Use and Def operands together.
-static bool hasTiedOperands(const Instruction &Instr, const Variable &Var) {
- bool HasUse = false;
- bool HasDef = false;
- for (const unsigned OpIndex : Var.TiedOperands) {
- const Operand &Op = Instr.Operands[OpIndex];
- if (Op.IsDef)
- HasDef = true;
- else
- HasUse = true;
- }
- return HasUse && HasDef;
-}
-
static llvm::SmallVector<const Variable *, 8>
-getTiedVariables(const Instruction &Instr) {
+getVariablesWithTiedOperands(const Instruction &Instr) {
llvm::SmallVector<const Variable *, 8> Result;
for (const auto &Var : Instr.Variables)
- if (hasTiedOperands(Instr, Var))
+ if (Var.hasTiedOperands())
Result.push_back(&Var);
return Result;
}
@@ -131,20 +98,70 @@ static void remove(llvm::BitVector &a, const llvm::BitVector &b) {
UopsBenchmarkRunner::~UopsBenchmarkRunner() = default;
-llvm::Expected<SnippetPrototype>
-UopsBenchmarkRunner::generatePrototype(unsigned Opcode) const {
- const auto &InstrDesc = State.getInstrInfo().get(Opcode);
- if (auto E = isInfeasible(InstrDesc))
- return std::move(E);
- const Instruction Instr(InstrDesc, RATC);
+UopsSnippetGenerator::~UopsSnippetGenerator() = default;
+
+void UopsSnippetGenerator::instantiateMemoryOperands(
+ const unsigned ScratchSpacePointerInReg,
+ std::vector<InstructionTemplate> &Instructions) const {
+ if (ScratchSpacePointerInReg == 0)
+ return; // no memory operands.
+ const auto &ET = State.getExegesisTarget();
+ const unsigned MemStep = ET.getMaxMemoryAccessSize();
+ const size_t OriginalInstructionsSize = Instructions.size();
+ size_t I = 0;
+ for (InstructionTemplate &IT : Instructions) {
+ ET.fillMemoryOperands(IT, ScratchSpacePointerInReg, I * MemStep);
+ ++I;
+ }
+
+ while (Instructions.size() < kMinNumDifferentAddresses) {
+ InstructionTemplate IT = Instructions[I % OriginalInstructionsSize];
+ ET.fillMemoryOperands(IT, ScratchSpacePointerInReg, I * MemStep);
+ ++I;
+ Instructions.push_back(std::move(IT));
+ }
+ assert(I * MemStep < BenchmarkRunner::ScratchSpace::kSize &&
+ "not enough scratch space");
+}
+
+llvm::Expected<std::vector<CodeTemplate>>
+UopsSnippetGenerator::generateCodeTemplates(const Instruction &Instr) const {
+ CodeTemplate CT;
+ const llvm::BitVector *ScratchSpaceAliasedRegs = nullptr;
+ if (Instr.hasMemoryOperands()) {
+ const auto &ET = State.getExegesisTarget();
+ CT.ScratchSpacePointerInReg =
+ ET.getScratchMemoryRegister(State.getTargetMachine().getTargetTriple());
+ if (CT.ScratchSpacePointerInReg == 0)
+ return llvm::make_error<BenchmarkFailure>(
+ "Infeasible : target does not support memory instructions");
+ ScratchSpaceAliasedRegs =
+ &State.getRATC().getRegister(CT.ScratchSpacePointerInReg).aliasedBits();
+ // If the instruction implicitly writes to ScratchSpacePointerInReg , abort.
+ // FIXME: We could make a copy of the scratch register.
+ for (const auto &Op : Instr.Operands) {
+ if (Op.isDef() && Op.isImplicitReg() &&
+ ScratchSpaceAliasedRegs->test(Op.getImplicitReg()))
+ return llvm::make_error<BenchmarkFailure>(
+ "Infeasible : memory instruction uses scratch memory register");
+ }
+ }
+
const AliasingConfigurations SelfAliasing(Instr, Instr);
+ InstructionTemplate IT(Instr);
if (SelfAliasing.empty()) {
- return generateUnconstrainedPrototype(Instr, "instruction is parallel");
+ CT.Info = "instruction is parallel, repeating a random one.";
+ CT.Instructions.push_back(std::move(IT));
+ instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
+ return getSingleton(std::move(CT));
}
if (SelfAliasing.hasImplicitAliasing()) {
- return generateUnconstrainedPrototype(Instr, "instruction is serial");
+ CT.Info = "instruction is serial, repeating a random one.";
+ CT.Instructions.push_back(std::move(IT));
+ instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
+ return getSingleton(std::move(CT));
}
- const auto TiedVariables = getTiedVariables(Instr);
+ const auto TiedVariables = getVariablesWithTiedOperands(Instr);
if (!TiedVariables.empty()) {
if (TiedVariables.size() > 1)
return llvm::make_error<llvm::StringError>(
@@ -152,84 +169,86 @@ UopsBenchmarkRunner::generatePrototype(unsigned Opcode) const {
llvm::inconvertibleErrorCode());
const Variable *Var = TiedVariables.front();
assert(Var);
- assert(!Var->TiedOperands.empty());
- const Operand &Op = Instr.Operands[Var->TiedOperands.front()];
- assert(Op.Tracker);
- SnippetPrototype Prototype;
- Prototype.Explanation =
- "instruction has tied variables using static renaming.";
- for (const llvm::MCPhysReg Reg : Op.Tracker->sourceBits().set_bits()) {
- Prototype.Snippet.emplace_back(Instr);
- Prototype.Snippet.back().getValueFor(*Var) =
- llvm::MCOperand::createReg(Reg);
+ const Operand &Op = Instr.getPrimaryOperand(*Var);
+ assert(Op.isReg());
+ CT.Info = "instruction has tied variables using static renaming.";
+ for (const llvm::MCPhysReg Reg :
+ Op.getRegisterAliasing().sourceBits().set_bits()) {
+ if (ScratchSpaceAliasedRegs && ScratchSpaceAliasedRegs->test(Reg))
+ continue; // Do not use the scratch memory address register.
+ InstructionTemplate TmpIT = IT;
+ TmpIT.getValueFor(*Var) = llvm::MCOperand::createReg(Reg);
+ CT.Instructions.push_back(std::move(TmpIT));
}
- return std::move(Prototype);
+ instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
+ return getSingleton(std::move(CT));
}
- InstructionInstance II(Instr);
+ const auto &ReservedRegisters = State.getRATC().reservedRegisters();
// No tied variables, we pick random values for defs.
llvm::BitVector Defs(State.getRegInfo().getNumRegs());
for (const auto &Op : Instr.Operands) {
- if (Op.Tracker && Op.IsExplicit && Op.IsDef) {
- auto PossibleRegisters = Op.Tracker->sourceBits();
- remove(PossibleRegisters, RATC.reservedRegisters());
+ if (Op.isReg() && Op.isExplicit() && Op.isDef() && !Op.isMemory()) {
+ auto PossibleRegisters = Op.getRegisterAliasing().sourceBits();
+ remove(PossibleRegisters, ReservedRegisters);
+ // Do not use the scratch memory address register.
+ if (ScratchSpaceAliasedRegs)
+ remove(PossibleRegisters, *ScratchSpaceAliasedRegs);
assert(PossibleRegisters.any() && "No register left to choose from");
const auto RandomReg = randomBit(PossibleRegisters);
Defs.set(RandomReg);
- II.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg);
+ IT.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg);
}
}
// And pick random use values that are not reserved and don't alias with defs.
const auto DefAliases = getAliasedBits(State.getRegInfo(), Defs);
for (const auto &Op : Instr.Operands) {
- if (Op.Tracker && Op.IsExplicit && !Op.IsDef) {
- auto PossibleRegisters = Op.Tracker->sourceBits();
- remove(PossibleRegisters, RATC.reservedRegisters());
+ if (Op.isReg() && Op.isExplicit() && Op.isUse() && !Op.isMemory()) {
+ auto PossibleRegisters = Op.getRegisterAliasing().sourceBits();
+ remove(PossibleRegisters, ReservedRegisters);
+ // Do not use the scratch memory address register.
+ if (ScratchSpaceAliasedRegs)
+ remove(PossibleRegisters, *ScratchSpaceAliasedRegs);
remove(PossibleRegisters, DefAliases);
assert(PossibleRegisters.any() && "No register left to choose from");
const auto RandomReg = randomBit(PossibleRegisters);
- II.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg);
+ IT.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg);
}
}
- SnippetPrototype Prototype;
- Prototype.Explanation =
+ CT.Info =
"instruction has no tied variables picking Uses different from defs";
- Prototype.Snippet.push_back(std::move(II));
- return std::move(Prototype);
+ CT.Instructions.push_back(std::move(IT));
+ instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
+ return getSingleton(std::move(CT));
}
-std::vector<BenchmarkMeasure>
-UopsBenchmarkRunner::runMeasurements(const ExecutableFunction &Function,
- const unsigned NumRepetitions) const {
- const auto &SchedModel = State.getSubtargetInfo().getSchedModel();
-
+llvm::Expected<std::vector<BenchmarkMeasure>>
+UopsBenchmarkRunner::runMeasurements(const FunctionExecutor &Executor) const {
std::vector<BenchmarkMeasure> Result;
- for (unsigned ProcResIdx = 1;
- ProcResIdx < SchedModel.getNumProcResourceKinds(); ++ProcResIdx) {
- const char *const PfmCounters = SchedModel.getExtraProcessorInfo()
- .PfmCounters.IssueCounters[ProcResIdx];
- if (!PfmCounters)
+ const PfmCountersInfo &PCI = State.getPfmCounters();
+ // Uops per port.
+ for (const auto *IssueCounter = PCI.IssueCounters,
+ *IssueCounterEnd = PCI.IssueCounters + PCI.NumIssueCounters;
+ IssueCounter != IssueCounterEnd; ++IssueCounter) {
+ if (!IssueCounter->Counter)
continue;
- // We sum counts when there are several counters for a single ProcRes
- // (e.g. P23 on SandyBridge).
- int64_t CounterValue = 0;
- llvm::SmallVector<llvm::StringRef, 2> CounterNames;
- llvm::StringRef(PfmCounters).split(CounterNames, ',');
- for (const auto &CounterName : CounterNames) {
- pfm::PerfEvent UopPerfEvent(CounterName);
- if (!UopPerfEvent.valid())
- llvm::report_fatal_error(
- llvm::Twine("invalid perf event ").concat(PfmCounters));
- pfm::Counter Counter(UopPerfEvent);
- Counter.start();
- Function();
- Counter.stop();
- CounterValue += Counter.read();
- }
- Result.push_back({llvm::itostr(ProcResIdx),
- static_cast<double>(CounterValue) / NumRepetitions,
- SchedModel.getProcResource(ProcResIdx)->Name});
+ auto ExpectedCounterValue = Executor.runAndMeasure(IssueCounter->Counter);
+ if (!ExpectedCounterValue)
+ return ExpectedCounterValue.takeError();
+ Result.push_back(BenchmarkMeasure::Create(IssueCounter->ProcResName,
+ *ExpectedCounterValue));
}
- return Result;
+ // NumMicroOps.
+ if (const char *const UopsCounter = PCI.UopsCounter) {
+ auto ExpectedCounterValue = Executor.runAndMeasure(UopsCounter);
+ if (!ExpectedCounterValue)
+ return ExpectedCounterValue.takeError();
+ Result.push_back(
+ BenchmarkMeasure::Create("NumMicroOps", *ExpectedCounterValue));
+ }
+ return std::move(Result);
}
+constexpr const size_t UopsSnippetGenerator::kMinNumDifferentAddresses;
+
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/lib/Uops.h b/tools/llvm-exegesis/lib/Uops.h
index c7b5709f7d9e..b2a5ea177f44 100644
--- a/tools/llvm-exegesis/lib/Uops.h
+++ b/tools/llvm-exegesis/lib/Uops.h
@@ -16,26 +16,64 @@
#define LLVM_TOOLS_LLVM_EXEGESIS_UOPS_H
#include "BenchmarkRunner.h"
+#include "SnippetGenerator.h"
+namespace llvm {
namespace exegesis {
+class UopsSnippetGenerator : public SnippetGenerator {
+public:
+ UopsSnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {}
+ ~UopsSnippetGenerator() override;
+
+ llvm::Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(const Instruction &Instr) const override;
+
+ static constexpr const size_t kMinNumDifferentAddresses = 6;
+
+private:
+ // Instantiates memory operands within a snippet.
+ // To make computations as parallel as possible, we generate independant
+ // memory locations for instructions that load and store. If there are less
+ // than kMinNumDifferentAddresses in the original snippet, we duplicate
+ // instructions until there are this number of instructions.
+ // For example, assuming kMinNumDifferentAddresses=5 and
+ // getMaxMemoryAccessSize()=64, if the original snippet is:
+ // mov eax, [memory]
+ // we might generate:
+ // mov eax, [rdi]
+ // mov eax, [rdi + 64]
+ // mov eax, [rdi + 128]
+ // mov eax, [rdi + 192]
+ // mov eax, [rdi + 256]
+ // If the original snippet is:
+ // mov eax, [memory]
+ // add eax, [memory]
+ // we might generate:
+ // mov eax, [rdi]
+ // add eax, [rdi + 64]
+ // mov eax, [rdi + 128]
+ // add eax, [rdi + 192]
+ // mov eax, [rdi + 256]
+ void instantiateMemoryOperands(
+ unsigned ScratchSpaceReg,
+ std::vector<InstructionTemplate> &SnippetTemplate) const;
+};
+
class UopsBenchmarkRunner : public BenchmarkRunner {
public:
UopsBenchmarkRunner(const LLVMState &State)
: BenchmarkRunner(State, InstructionBenchmark::Uops) {}
~UopsBenchmarkRunner() override;
- llvm::Expected<SnippetPrototype>
- generatePrototype(unsigned Opcode) const override;
+ static constexpr const size_t kMinNumDifferentAddresses = 6;
private:
- llvm::Error isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const;
-
- std::vector<BenchmarkMeasure>
- runMeasurements(const ExecutableFunction &EF,
- const unsigned NumRepetitions) const override;
+ llvm::Expected<std::vector<BenchmarkMeasure>>
+ runMeasurements(const FunctionExecutor &Executor) const override;
};
} // namespace exegesis
+} // namespace llvm
#endif // LLVM_TOOLS_LLVM_EXEGESIS_UOPS_H
diff --git a/tools/llvm-exegesis/lib/X86/Target.cpp b/tools/llvm-exegesis/lib/X86/Target.cpp
index 5c417e325f5d..43bf9b750580 100644
--- a/tools/llvm-exegesis/lib/X86/Target.cpp
+++ b/tools/llvm-exegesis/lib/X86/Target.cpp
@@ -17,227 +17,526 @@
#include "X86Subtarget.h"
#include "llvm/MC/MCInstBuilder.h"
+namespace llvm {
namespace exegesis {
-namespace {
+// Returns an error if we cannot handle the memory references in this
+// instruction.
+static Error isInvalidMemoryInstr(const Instruction &Instr) {
+ switch (Instr.Description->TSFlags & X86II::FormMask) {
+ default:
+ llvm_unreachable("Unknown FormMask value");
+ // These have no memory access.
+ case X86II::Pseudo:
+ case X86II::RawFrm:
+ case X86II::MRMDestReg:
+ case X86II::MRMSrcReg:
+ case X86II::MRMSrcReg4VOp3:
+ case X86II::MRMSrcRegOp4:
+ case X86II::MRMXr:
+ case X86II::MRM0r:
+ case X86II::MRM1r:
+ case X86II::MRM2r:
+ case X86II::MRM3r:
+ case X86II::MRM4r:
+ case X86II::MRM5r:
+ case X86II::MRM6r:
+ case X86II::MRM7r:
+ case X86II::MRM_C0:
+ case X86II::MRM_C1:
+ case X86II::MRM_C2:
+ case X86II::MRM_C3:
+ case X86II::MRM_C4:
+ case X86II::MRM_C5:
+ case X86II::MRM_C6:
+ case X86II::MRM_C7:
+ case X86II::MRM_C8:
+ case X86II::MRM_C9:
+ case X86II::MRM_CA:
+ case X86II::MRM_CB:
+ case X86II::MRM_CC:
+ case X86II::MRM_CD:
+ case X86II::MRM_CE:
+ case X86II::MRM_CF:
+ case X86II::MRM_D0:
+ case X86II::MRM_D1:
+ case X86II::MRM_D2:
+ case X86II::MRM_D3:
+ case X86II::MRM_D4:
+ case X86II::MRM_D5:
+ case X86II::MRM_D6:
+ case X86II::MRM_D7:
+ case X86II::MRM_D8:
+ case X86II::MRM_D9:
+ case X86II::MRM_DA:
+ case X86II::MRM_DB:
+ case X86II::MRM_DC:
+ case X86II::MRM_DD:
+ case X86II::MRM_DE:
+ case X86II::MRM_DF:
+ case X86II::MRM_E0:
+ case X86II::MRM_E1:
+ case X86II::MRM_E2:
+ case X86II::MRM_E3:
+ case X86II::MRM_E4:
+ case X86II::MRM_E5:
+ case X86II::MRM_E6:
+ case X86II::MRM_E7:
+ case X86II::MRM_E8:
+ case X86II::MRM_E9:
+ case X86II::MRM_EA:
+ case X86II::MRM_EB:
+ case X86II::MRM_EC:
+ case X86II::MRM_ED:
+ case X86II::MRM_EE:
+ case X86II::MRM_EF:
+ case X86II::MRM_F0:
+ case X86II::MRM_F1:
+ case X86II::MRM_F2:
+ case X86II::MRM_F3:
+ case X86II::MRM_F4:
+ case X86II::MRM_F5:
+ case X86II::MRM_F6:
+ case X86II::MRM_F7:
+ case X86II::MRM_F8:
+ case X86II::MRM_F9:
+ case X86II::MRM_FA:
+ case X86II::MRM_FB:
+ case X86II::MRM_FC:
+ case X86II::MRM_FD:
+ case X86II::MRM_FE:
+ case X86II::MRM_FF:
+ case X86II::RawFrmImm8:
+ return Error::success();
+ case X86II::AddRegFrm:
+ return (Instr.Description->Opcode == X86::POP16r || Instr.Description->Opcode == X86::POP32r ||
+ Instr.Description->Opcode == X86::PUSH16r || Instr.Description->Opcode == X86::PUSH32r)
+ ? make_error<BenchmarkFailure>(
+ "unsupported opcode: unsupported memory access")
+ : Error::success();
+ // These access memory and are handled.
+ case X86II::MRMDestMem:
+ case X86II::MRMSrcMem:
+ case X86II::MRMSrcMem4VOp3:
+ case X86II::MRMSrcMemOp4:
+ case X86II::MRMXm:
+ case X86II::MRM0m:
+ case X86II::MRM1m:
+ case X86II::MRM2m:
+ case X86II::MRM3m:
+ case X86II::MRM4m:
+ case X86II::MRM5m:
+ case X86II::MRM6m:
+ case X86II::MRM7m:
+ return Error::success();
+ // These access memory and are not handled yet.
+ case X86II::RawFrmImm16:
+ case X86II::RawFrmMemOffs:
+ case X86II::RawFrmSrc:
+ case X86II::RawFrmDst:
+ case X86II::RawFrmDstSrc:
+ return make_error<BenchmarkFailure>(
+ "unsupported opcode: non uniform memory access");
+ }
+}
-// Common code for X86 Uops and Latency runners.
-template <typename Impl> class X86BenchmarkRunner : public Impl {
- using Impl::Impl;
-
- llvm::Expected<SnippetPrototype>
- generatePrototype(unsigned Opcode) const override {
- // Test whether we can generate a snippet for this instruction.
- const auto &InstrInfo = this->State.getInstrInfo();
- const auto OpcodeName = InstrInfo.getName(Opcode);
- if (OpcodeName.startswith("POPF") || OpcodeName.startswith("PUSHF") ||
- OpcodeName.startswith("ADJCALLSTACK")) {
+static llvm::Error IsInvalidOpcode(const Instruction &Instr) {
+ const auto OpcodeName = Instr.Name;
+ if ((Instr.Description->TSFlags & X86II::FormMask) == X86II::Pseudo)
+ return llvm::make_error<BenchmarkFailure>(
+ "unsupported opcode: pseudo instruction");
+ if (OpcodeName.startswith("POPF") || OpcodeName.startswith("PUSHF") ||
+ OpcodeName.startswith("ADJCALLSTACK"))
+ return llvm::make_error<BenchmarkFailure>(
+ "unsupported opcode: Push/Pop/AdjCallStack");
+ if (llvm::Error Error = isInvalidMemoryInstr(Instr))
+ return Error;
+ // We do not handle instructions with OPERAND_PCREL.
+ for (const Operand &Op : Instr.Operands)
+ if (Op.isExplicit() &&
+ Op.getExplicitOperandInfo().OperandType == llvm::MCOI::OPERAND_PCREL)
return llvm::make_error<BenchmarkFailure>(
- "Unsupported opcode: Push/Pop/AdjCallStack");
- }
+ "unsupported opcode: PC relative operand");
+ // We do not handle second-form X87 instructions. We only handle first-form
+ // ones (_Fp), see comment in X86InstrFPStack.td.
+ for (const Operand &Op : Instr.Operands)
+ if (Op.isReg() && Op.isExplicit() &&
+ Op.getExplicitOperandInfo().RegClass == llvm::X86::RSTRegClassID)
+ return llvm::make_error<BenchmarkFailure>(
+ "unsupported second-form X87 instruction");
+ return llvm::Error::success();
+}
- // Handle X87.
- const auto &InstrDesc = InstrInfo.get(Opcode);
- const unsigned FPInstClass = InstrDesc.TSFlags & llvm::X86II::FPTypeMask;
- const Instruction Instr(InstrDesc, this->RATC);
- switch (FPInstClass) {
- case llvm::X86II::NotFP:
- break;
- case llvm::X86II::ZeroArgFP:
- return llvm::make_error<BenchmarkFailure>("Unsupported x87 ZeroArgFP");
- case llvm::X86II::OneArgFP:
- return llvm::make_error<BenchmarkFailure>("Unsupported x87 OneArgFP");
- case llvm::X86II::OneArgFPRW:
- case llvm::X86II::TwoArgFP: {
- // These are instructions like
- // - `ST(0) = fsqrt(ST(0))` (OneArgFPRW)
- // - `ST(0) = ST(0) + ST(i)` (TwoArgFP)
- // They are intrinsically serial and do not modify the state of the stack.
- // We generate the same code for latency and uops.
- return this->generateSelfAliasingPrototype(Instr);
- }
- case llvm::X86II::CompareFP:
- return Impl::handleCompareFP(Instr);
- case llvm::X86II::CondMovFP:
- return Impl::handleCondMovFP(Instr);
- case llvm::X86II::SpecialFP:
- return llvm::make_error<BenchmarkFailure>("Unsupported x87 SpecialFP");
- default:
- llvm_unreachable("Unknown FP Type!");
- }
+static unsigned getX86FPFlags(const Instruction &Instr) {
+ return Instr.Description->TSFlags & llvm::X86II::FPTypeMask;
+}
- // Fallback to generic implementation.
- return Impl::Base::generatePrototype(Opcode);
- }
+namespace {
+class X86LatencySnippetGenerator : public LatencySnippetGenerator {
+public:
+ using LatencySnippetGenerator::LatencySnippetGenerator;
+
+ llvm::Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(const Instruction &Instr) const override;
};
+} // namespace
-class X86LatencyImpl : public LatencyBenchmarkRunner {
-protected:
- using Base = LatencyBenchmarkRunner;
- using Base::Base;
- llvm::Expected<SnippetPrototype>
- handleCompareFP(const Instruction &Instr) const {
- return llvm::make_error<BenchmarkFailure>("Unsupported x87 CompareFP");
- }
- llvm::Expected<SnippetPrototype>
- handleCondMovFP(const Instruction &Instr) const {
- return llvm::make_error<BenchmarkFailure>("Unsupported x87 CondMovFP");
+llvm::Expected<std::vector<CodeTemplate>>
+X86LatencySnippetGenerator::generateCodeTemplates(
+ const Instruction &Instr) const {
+ if (auto E = IsInvalidOpcode(Instr))
+ return std::move(E);
+
+ switch (getX86FPFlags(Instr)) {
+ case llvm::X86II::NotFP:
+ return LatencySnippetGenerator::generateCodeTemplates(Instr);
+ case llvm::X86II::ZeroArgFP:
+ case llvm::X86II::OneArgFP:
+ case llvm::X86II::SpecialFP:
+ case llvm::X86II::CompareFP:
+ case llvm::X86II::CondMovFP:
+ return llvm::make_error<BenchmarkFailure>("Unsupported x87 Instruction");
+ case llvm::X86II::OneArgFPRW:
+ case llvm::X86II::TwoArgFP:
+ // These are instructions like
+ // - `ST(0) = fsqrt(ST(0))` (OneArgFPRW)
+ // - `ST(0) = ST(0) + ST(i)` (TwoArgFP)
+ // They are intrinsically serial and do not modify the state of the stack.
+ return generateSelfAliasingCodeTemplates(Instr);
+ default:
+ llvm_unreachable("Unknown FP Type!");
}
+}
+
+namespace {
+class X86UopsSnippetGenerator : public UopsSnippetGenerator {
+public:
+ using UopsSnippetGenerator::UopsSnippetGenerator;
+
+ llvm::Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(const Instruction &Instr) const override;
};
+} // namespace
-class X86UopsImpl : public UopsBenchmarkRunner {
-protected:
- using Base = UopsBenchmarkRunner;
- using Base::Base;
- // We can compute uops for any FP instruction that does not grow or shrink the
- // stack (either do not touch the stack or push as much as they pop).
- llvm::Expected<SnippetPrototype>
- handleCompareFP(const Instruction &Instr) const {
- return generateUnconstrainedPrototype(
+llvm::Expected<std::vector<CodeTemplate>>
+X86UopsSnippetGenerator::generateCodeTemplates(
+ const Instruction &Instr) const {
+ if (auto E = IsInvalidOpcode(Instr))
+ return std::move(E);
+
+ switch (getX86FPFlags(Instr)) {
+ case llvm::X86II::NotFP:
+ return UopsSnippetGenerator::generateCodeTemplates(Instr);
+ case llvm::X86II::ZeroArgFP:
+ case llvm::X86II::OneArgFP:
+ case llvm::X86II::SpecialFP:
+ return llvm::make_error<BenchmarkFailure>("Unsupported x87 Instruction");
+ case llvm::X86II::OneArgFPRW:
+ case llvm::X86II::TwoArgFP:
+ // These are instructions like
+ // - `ST(0) = fsqrt(ST(0))` (OneArgFPRW)
+ // - `ST(0) = ST(0) + ST(i)` (TwoArgFP)
+ // They are intrinsically serial and do not modify the state of the stack.
+ // We generate the same code for latency and uops.
+ return generateSelfAliasingCodeTemplates(Instr);
+ case llvm::X86II::CompareFP:
+ case llvm::X86II::CondMovFP:
+ // We can compute uops for any FP instruction that does not grow or shrink
+ // the stack (either do not touch the stack or push as much as they pop).
+ return generateUnconstrainedCodeTemplates(
Instr, "instruction does not grow/shrink the FP stack");
+ default:
+ llvm_unreachable("Unknown FP Type!");
}
- llvm::Expected<SnippetPrototype>
- handleCondMovFP(const Instruction &Instr) const {
- return generateUnconstrainedPrototype(
- Instr, "instruction does not grow/shrink the FP stack");
+}
+
+static unsigned getLoadImmediateOpcode(unsigned RegBitWidth) {
+ switch (RegBitWidth) {
+ case 8:
+ return llvm::X86::MOV8ri;
+ case 16:
+ return llvm::X86::MOV16ri;
+ case 32:
+ return llvm::X86::MOV32ri;
+ case 64:
+ return llvm::X86::MOV64ri;
}
-};
+ llvm_unreachable("Invalid Value Width");
+}
-class ExegesisX86Target : public ExegesisTarget {
- void addTargetSpecificPasses(llvm::PassManagerBase &PM) const override {
- // Lowers FP pseudo-instructions, e.g. ABS_Fp32 -> ABS_F.
- PM.add(llvm::createX86FloatingPointStackifierPass());
+// Generates instruction to load an immediate value into a register.
+static llvm::MCInst loadImmediate(unsigned Reg, unsigned RegBitWidth,
+ const llvm::APInt &Value) {
+ if (Value.getBitWidth() > RegBitWidth)
+ llvm_unreachable("Value must fit in the Register");
+ return llvm::MCInstBuilder(getLoadImmediateOpcode(RegBitWidth))
+ .addReg(Reg)
+ .addImm(Value.getZExtValue());
+}
+
+// Allocates scratch memory on the stack.
+static llvm::MCInst allocateStackSpace(unsigned Bytes) {
+ return llvm::MCInstBuilder(llvm::X86::SUB64ri8)
+ .addReg(llvm::X86::RSP)
+ .addReg(llvm::X86::RSP)
+ .addImm(Bytes);
+}
+
+// Fills scratch memory at offset `OffsetBytes` with value `Imm`.
+static llvm::MCInst fillStackSpace(unsigned MovOpcode, unsigned OffsetBytes,
+ uint64_t Imm) {
+ return llvm::MCInstBuilder(MovOpcode)
+ // Address = ESP
+ .addReg(llvm::X86::RSP) // BaseReg
+ .addImm(1) // ScaleAmt
+ .addReg(0) // IndexReg
+ .addImm(OffsetBytes) // Disp
+ .addReg(0) // Segment
+ // Immediate.
+ .addImm(Imm);
+}
+
+// Loads scratch memory into register `Reg` using opcode `RMOpcode`.
+static llvm::MCInst loadToReg(unsigned Reg, unsigned RMOpcode) {
+ return llvm::MCInstBuilder(RMOpcode)
+ .addReg(Reg)
+ // Address = ESP
+ .addReg(llvm::X86::RSP) // BaseReg
+ .addImm(1) // ScaleAmt
+ .addReg(0) // IndexReg
+ .addImm(0) // Disp
+ .addReg(0); // Segment
+}
+
+// Releases scratch memory.
+static llvm::MCInst releaseStackSpace(unsigned Bytes) {
+ return llvm::MCInstBuilder(llvm::X86::ADD64ri8)
+ .addReg(llvm::X86::RSP)
+ .addReg(llvm::X86::RSP)
+ .addImm(Bytes);
+}
+
+// Reserves some space on the stack, fills it with the content of the provided
+// constant and provide methods to load the stack value into a register.
+namespace {
+struct ConstantInliner {
+ explicit ConstantInliner(const llvm::APInt &Constant) : Constant_(Constant) {}
+
+ std::vector<llvm::MCInst> loadAndFinalize(unsigned Reg, unsigned RegBitWidth,
+ unsigned Opcode);
+
+ std::vector<llvm::MCInst> loadX87STAndFinalize(unsigned Reg);
+
+ std::vector<llvm::MCInst> loadX87FPAndFinalize(unsigned Reg);
+
+ std::vector<llvm::MCInst> popFlagAndFinalize();
+
+private:
+ ConstantInliner &add(const llvm::MCInst &Inst) {
+ Instructions.push_back(Inst);
+ return *this;
}
- std::vector<llvm::MCInst> setRegToConstant(const llvm::MCSubtargetInfo &STI,
- unsigned Reg) const override {
- // GPR.
- if (llvm::X86::GR8RegClass.contains(Reg))
- return {llvm::MCInstBuilder(llvm::X86::MOV8ri).addReg(Reg).addImm(1)};
- if (llvm::X86::GR16RegClass.contains(Reg))
- return {llvm::MCInstBuilder(llvm::X86::MOV16ri).addReg(Reg).addImm(1)};
- if (llvm::X86::GR32RegClass.contains(Reg))
- return {llvm::MCInstBuilder(llvm::X86::MOV32ri).addReg(Reg).addImm(1)};
- if (llvm::X86::GR64RegClass.contains(Reg))
- return {llvm::MCInstBuilder(llvm::X86::MOV64ri32).addReg(Reg).addImm(1)};
- // MMX.
- if (llvm::X86::VR64RegClass.contains(Reg))
- return setVectorRegToConstant(Reg, 8, llvm::X86::MMX_MOVQ64rm);
- // {X,Y,Z}MM.
- if (llvm::X86::VR128XRegClass.contains(Reg)) {
- if (STI.getFeatureBits()[llvm::X86::FeatureAVX512])
- return setVectorRegToConstant(Reg, 16, llvm::X86::VMOVDQU32Z128rm);
- if (STI.getFeatureBits()[llvm::X86::FeatureAVX])
- return setVectorRegToConstant(Reg, 16, llvm::X86::VMOVDQUrm);
- return setVectorRegToConstant(Reg, 16, llvm::X86::MOVDQUrm);
- }
- if (llvm::X86::VR256XRegClass.contains(Reg)) {
- if (STI.getFeatureBits()[llvm::X86::FeatureAVX512])
- return setVectorRegToConstant(Reg, 32, llvm::X86::VMOVDQU32Z256rm);
- return setVectorRegToConstant(Reg, 32, llvm::X86::VMOVDQUYrm);
- }
- if (llvm::X86::VR512RegClass.contains(Reg))
- return setVectorRegToConstant(Reg, 64, llvm::X86::VMOVDQU32Zrm);
- // X87.
- if (llvm::X86::RFP32RegClass.contains(Reg) ||
- llvm::X86::RFP64RegClass.contains(Reg) ||
- llvm::X86::RFP80RegClass.contains(Reg))
- return setVectorRegToConstant(Reg, 8, llvm::X86::LD_Fp64m);
- if (Reg == llvm::X86::EFLAGS) {
- // Set all flags to 0 but the bits that are "reserved and set to 1".
- constexpr const uint32_t kImmValue = 0x00007002u;
- std::vector<llvm::MCInst> Result;
- Result.push_back(allocateStackSpace(8));
- Result.push_back(fillStackSpace(llvm::X86::MOV64mi32, 0, kImmValue));
- Result.push_back(llvm::MCInstBuilder(llvm::X86::POPF64)); // Also pops.
- return Result;
- }
- return {};
+ void initStack(unsigned Bytes);
+
+ static constexpr const unsigned kF80Bytes = 10; // 80 bits.
+
+ llvm::APInt Constant_;
+ std::vector<llvm::MCInst> Instructions;
+};
+} // namespace
+
+std::vector<llvm::MCInst> ConstantInliner::loadAndFinalize(unsigned Reg,
+ unsigned RegBitWidth,
+ unsigned Opcode) {
+ assert((RegBitWidth & 7) == 0 && "RegBitWidth must be a multiple of 8 bits");
+ initStack(RegBitWidth / 8);
+ add(loadToReg(Reg, Opcode));
+ add(releaseStackSpace(RegBitWidth / 8));
+ return std::move(Instructions);
+}
+
+std::vector<llvm::MCInst> ConstantInliner::loadX87STAndFinalize(unsigned Reg) {
+ initStack(kF80Bytes);
+ add(llvm::MCInstBuilder(llvm::X86::LD_F80m)
+ // Address = ESP
+ .addReg(llvm::X86::RSP) // BaseReg
+ .addImm(1) // ScaleAmt
+ .addReg(0) // IndexReg
+ .addImm(0) // Disp
+ .addReg(0)); // Segment
+ if (Reg != llvm::X86::ST0)
+ add(llvm::MCInstBuilder(llvm::X86::ST_Frr).addReg(Reg));
+ add(releaseStackSpace(kF80Bytes));
+ return std::move(Instructions);
+}
+
+std::vector<llvm::MCInst> ConstantInliner::loadX87FPAndFinalize(unsigned Reg) {
+ initStack(kF80Bytes);
+ add(llvm::MCInstBuilder(llvm::X86::LD_Fp80m)
+ .addReg(Reg)
+ // Address = ESP
+ .addReg(llvm::X86::RSP) // BaseReg
+ .addImm(1) // ScaleAmt
+ .addReg(0) // IndexReg
+ .addImm(0) // Disp
+ .addReg(0)); // Segment
+ add(releaseStackSpace(kF80Bytes));
+ return std::move(Instructions);
+}
+
+std::vector<llvm::MCInst> ConstantInliner::popFlagAndFinalize() {
+ initStack(8);
+ add(llvm::MCInstBuilder(llvm::X86::POPF64));
+ return std::move(Instructions);
+}
+
+void ConstantInliner::initStack(unsigned Bytes) {
+ assert(Constant_.getBitWidth() <= Bytes * 8 &&
+ "Value does not have the correct size");
+ const llvm::APInt WideConstant = Constant_.getBitWidth() < Bytes * 8
+ ? Constant_.sext(Bytes * 8)
+ : Constant_;
+ add(allocateStackSpace(Bytes));
+ size_t ByteOffset = 0;
+ for (; Bytes - ByteOffset >= 4; ByteOffset += 4)
+ add(fillStackSpace(
+ llvm::X86::MOV32mi, ByteOffset,
+ WideConstant.extractBits(32, ByteOffset * 8).getZExtValue()));
+ if (Bytes - ByteOffset >= 2) {
+ add(fillStackSpace(
+ llvm::X86::MOV16mi, ByteOffset,
+ WideConstant.extractBits(16, ByteOffset * 8).getZExtValue()));
+ ByteOffset += 2;
}
+ if (Bytes - ByteOffset >= 1)
+ add(fillStackSpace(
+ llvm::X86::MOV8mi, ByteOffset,
+ WideConstant.extractBits(8, ByteOffset * 8).getZExtValue()));
+}
- std::unique_ptr<BenchmarkRunner>
- createLatencyBenchmarkRunner(const LLVMState &State) const override {
- return llvm::make_unique<X86BenchmarkRunner<X86LatencyImpl>>(State);
+#include "X86GenExegesis.inc"
+
+namespace {
+class ExegesisX86Target : public ExegesisTarget {
+public:
+ ExegesisX86Target() : ExegesisTarget(X86CpuPfmCounters) {}
+
+private:
+ void addTargetSpecificPasses(llvm::PassManagerBase &PM) const override;
+
+ unsigned getScratchMemoryRegister(const llvm::Triple &TT) const override;
+
+ unsigned getMaxMemoryAccessSize() const override { return 64; }
+
+ void fillMemoryOperands(InstructionTemplate &IT, unsigned Reg,
+ unsigned Offset) const override;
+
+ std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI,
+ unsigned Reg,
+ const llvm::APInt &Value) const override;
+
+ std::unique_ptr<SnippetGenerator>
+ createLatencySnippetGenerator(const LLVMState &State) const override {
+ return llvm::make_unique<X86LatencySnippetGenerator>(State);
}
- std::unique_ptr<BenchmarkRunner>
- createUopsBenchmarkRunner(const LLVMState &State) const override {
- return llvm::make_unique<X86BenchmarkRunner<X86UopsImpl>>(State);
+ std::unique_ptr<SnippetGenerator>
+ createUopsSnippetGenerator(const LLVMState &State) const override {
+ return llvm::make_unique<X86UopsSnippetGenerator>(State);
}
bool matchesArch(llvm::Triple::ArchType Arch) const override {
return Arch == llvm::Triple::x86_64 || Arch == llvm::Triple::x86;
}
+};
+} // namespace
-private:
- // setRegToConstant() specialized for a vector register of size
- // `RegSizeBytes`. `RMOpcode` is the opcode used to do a memory -> vector
- // register load.
- static std::vector<llvm::MCInst>
- setVectorRegToConstant(const unsigned Reg, const unsigned RegSizeBytes,
- const unsigned RMOpcode) {
- // There is no instruction to directly set XMM, go through memory.
- // Since vector values can be interpreted as integers of various sizes (8
- // to 64 bits) as well as floats and double, so we chose an immediate
- // value that has set bits for all byte values and is a normal float/
- // double. 0x40404040 is ~32.5 when interpreted as a double and ~3.0f when
- // interpreted as a float.
- constexpr const uint32_t kImmValue = 0x40404040u;
- std::vector<llvm::MCInst> Result;
- Result.push_back(allocateStackSpace(RegSizeBytes));
- constexpr const unsigned kMov32NumBytes = 4;
- for (unsigned Disp = 0; Disp < RegSizeBytes; Disp += kMov32NumBytes) {
- Result.push_back(fillStackSpace(llvm::X86::MOV32mi, Disp, kImmValue));
- }
- Result.push_back(loadToReg(Reg, RMOpcode));
- Result.push_back(releaseStackSpace(RegSizeBytes));
- return Result;
- }
+void ExegesisX86Target::addTargetSpecificPasses(
+ llvm::PassManagerBase &PM) const {
+ // Lowers FP pseudo-instructions, e.g. ABS_Fp32 -> ABS_F.
+ PM.add(llvm::createX86FloatingPointStackifierPass());
+}
- // Allocates scratch memory on the stack.
- static llvm::MCInst allocateStackSpace(unsigned Bytes) {
- return llvm::MCInstBuilder(llvm::X86::SUB64ri8)
- .addReg(llvm::X86::RSP)
- .addReg(llvm::X86::RSP)
- .addImm(Bytes);
+unsigned
+ExegesisX86Target::getScratchMemoryRegister(const llvm::Triple &TT) const {
+ if (!TT.isArch64Bit()) {
+ // FIXME: This would require popping from the stack, so we would have to
+ // add some additional setup code.
+ return 0;
}
+ return TT.isOSWindows() ? llvm::X86::RCX : llvm::X86::RDI;
+}
- // Fills scratch memory at offset `OffsetBytes` with value `Imm`.
- static llvm::MCInst fillStackSpace(unsigned MovOpcode, unsigned OffsetBytes,
- uint64_t Imm) {
- return llvm::MCInstBuilder(MovOpcode)
- // Address = ESP
- .addReg(llvm::X86::RSP) // BaseReg
- .addImm(1) // ScaleAmt
- .addReg(0) // IndexReg
- .addImm(OffsetBytes) // Disp
- .addReg(0) // Segment
- // Immediate.
- .addImm(Imm);
+void ExegesisX86Target::fillMemoryOperands(InstructionTemplate &IT,
+ unsigned Reg,
+ unsigned Offset) const {
+ assert(!isInvalidMemoryInstr(IT.Instr) &&
+ "fillMemoryOperands requires a valid memory instruction");
+ int MemOpIdx = X86II::getMemoryOperandNo(IT.Instr.Description->TSFlags);
+ assert(MemOpIdx >= 0 && "invalid memory operand index");
+ // getMemoryOperandNo() ignores tied operands, so we have to add them back.
+ for (unsigned I = 0; I <= static_cast<unsigned>(MemOpIdx); ++I) {
+ const auto &Op = IT.Instr.Operands[I];
+ if (Op.isTied() && Op.getTiedToIndex() < I) {
+ ++MemOpIdx;
+ }
}
+ // Now fill in the memory operands.
+ const auto SetOp = [&IT](int OpIdx, const MCOperand &OpVal) {
+ const auto Op = IT.Instr.Operands[OpIdx];
+ assert(Op.isMemory() && Op.isExplicit() && "invalid memory pattern");
+ IT.getValueFor(Op) = OpVal;
+ };
+ SetOp(MemOpIdx + 0, MCOperand::createReg(Reg)); // BaseReg
+ SetOp(MemOpIdx + 1, MCOperand::createImm(1)); // ScaleAmt
+ SetOp(MemOpIdx + 2, MCOperand::createReg(0)); // IndexReg
+ SetOp(MemOpIdx + 3, MCOperand::createImm(Offset)); // Disp
+ SetOp(MemOpIdx + 4, MCOperand::createReg(0)); // Segment
+}
- // Loads scratch memory into register `Reg` using opcode `RMOpcode`.
- static llvm::MCInst loadToReg(unsigned Reg, unsigned RMOpcode) {
- return llvm::MCInstBuilder(RMOpcode)
- .addReg(Reg)
- // Address = ESP
- .addReg(llvm::X86::RSP) // BaseReg
- .addImm(1) // ScaleAmt
- .addReg(0) // IndexReg
- .addImm(0) // Disp
- .addReg(0); // Segment
+std::vector<llvm::MCInst>
+ExegesisX86Target::setRegTo(const llvm::MCSubtargetInfo &STI, unsigned Reg,
+ const llvm::APInt &Value) const {
+ if (llvm::X86::GR8RegClass.contains(Reg))
+ return {loadImmediate(Reg, 8, Value)};
+ if (llvm::X86::GR16RegClass.contains(Reg))
+ return {loadImmediate(Reg, 16, Value)};
+ if (llvm::X86::GR32RegClass.contains(Reg))
+ return {loadImmediate(Reg, 32, Value)};
+ if (llvm::X86::GR64RegClass.contains(Reg))
+ return {loadImmediate(Reg, 64, Value)};
+ ConstantInliner CI(Value);
+ if (llvm::X86::VR64RegClass.contains(Reg))
+ return CI.loadAndFinalize(Reg, 64, llvm::X86::MMX_MOVQ64rm);
+ if (llvm::X86::VR128XRegClass.contains(Reg)) {
+ if (STI.getFeatureBits()[llvm::X86::FeatureAVX512])
+ return CI.loadAndFinalize(Reg, 128, llvm::X86::VMOVDQU32Z128rm);
+ if (STI.getFeatureBits()[llvm::X86::FeatureAVX])
+ return CI.loadAndFinalize(Reg, 128, llvm::X86::VMOVDQUrm);
+ return CI.loadAndFinalize(Reg, 128, llvm::X86::MOVDQUrm);
}
-
- // Releases scratch memory.
- static llvm::MCInst releaseStackSpace(unsigned Bytes) {
- return llvm::MCInstBuilder(llvm::X86::ADD64ri8)
- .addReg(llvm::X86::RSP)
- .addReg(llvm::X86::RSP)
- .addImm(Bytes);
+ if (llvm::X86::VR256XRegClass.contains(Reg)) {
+ if (STI.getFeatureBits()[llvm::X86::FeatureAVX512])
+ return CI.loadAndFinalize(Reg, 256, llvm::X86::VMOVDQU32Z256rm);
+ if (STI.getFeatureBits()[llvm::X86::FeatureAVX])
+ return CI.loadAndFinalize(Reg, 256, llvm::X86::VMOVDQUYrm);
}
-};
-
-} // namespace
+ if (llvm::X86::VR512RegClass.contains(Reg))
+ if (STI.getFeatureBits()[llvm::X86::FeatureAVX512])
+ return CI.loadAndFinalize(Reg, 512, llvm::X86::VMOVDQU32Zrm);
+ if (llvm::X86::RSTRegClass.contains(Reg)) {
+ return CI.loadX87STAndFinalize(Reg);
+ }
+ if (llvm::X86::RFP32RegClass.contains(Reg) ||
+ llvm::X86::RFP64RegClass.contains(Reg) ||
+ llvm::X86::RFP80RegClass.contains(Reg)) {
+ return CI.loadX87FPAndFinalize(Reg);
+ }
+ if (Reg == llvm::X86::EFLAGS)
+ return CI.popFlagAndFinalize();
+ return {}; // Not yet implemented.
+}
static ExegesisTarget *getTheExegesisX86Target() {
static ExegesisX86Target Target;
@@ -249,3 +548,4 @@ void InitializeX86ExegesisTarget() {
}
} // namespace exegesis
+} // namespace llvm
diff --git a/tools/llvm-exegesis/llvm-exegesis.cpp b/tools/llvm-exegesis/llvm-exegesis.cpp
index 6b626b0eaa34..a28e68ec006d 100644
--- a/tools/llvm-exegesis/llvm-exegesis.cpp
+++ b/tools/llvm-exegesis/llvm-exegesis.cpp
@@ -22,101 +22,299 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Twine.h"
#include "llvm/MC/MCInstBuilder.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCParser/MCAsmParser.h"
+#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include <algorithm>
-#include <random>
#include <string>
-#include <unordered_map>
-
-static llvm::cl::opt<unsigned>
- OpcodeIndex("opcode-index", llvm::cl::desc("opcode to measure, by index"),
- llvm::cl::init(0));
-
-static llvm::cl::opt<std::string>
- OpcodeName("opcode-name", llvm::cl::desc("opcode to measure, by name"),
- llvm::cl::init(""));
-
-static llvm::cl::opt<std::string>
- BenchmarkFile("benchmarks-file", llvm::cl::desc(""), llvm::cl::init(""));
-
-static llvm::cl::opt<exegesis::InstructionBenchmark::ModeE> BenchmarkMode(
- "mode", llvm::cl::desc("the mode to run"),
- llvm::cl::values(clEnumValN(exegesis::InstructionBenchmark::Latency,
- "latency", "Instruction Latency"),
- clEnumValN(exegesis::InstructionBenchmark::Uops, "uops",
- "Uop Decomposition"),
- // When not asking for a specific benchmark mode, we'll
- // analyse the results.
- clEnumValN(exegesis::InstructionBenchmark::Unknown,
- "analysis", "Analysis")));
-
-static llvm::cl::opt<unsigned>
+
+namespace llvm {
+namespace exegesis {
+
+static cl::opt<int> OpcodeIndex("opcode-index",
+ cl::desc("opcode to measure, by index"),
+ cl::init(0));
+
+static cl::opt<std::string>
+ OpcodeNames("opcode-name",
+ cl::desc("comma-separated list of opcodes to measure, by name"),
+ cl::init(""));
+
+static cl::opt<std::string> SnippetsFile("snippets-file",
+ cl::desc("code snippets to measure"),
+ cl::init(""));
+
+static cl::opt<std::string> BenchmarkFile("benchmarks-file", cl::desc(""),
+ cl::init(""));
+
+static cl::opt<exegesis::InstructionBenchmark::ModeE>
+ BenchmarkMode("mode", cl::desc("the mode to run"),
+ cl::values(clEnumValN(exegesis::InstructionBenchmark::Latency,
+ "latency", "Instruction Latency"),
+ clEnumValN(exegesis::InstructionBenchmark::Uops,
+ "uops", "Uop Decomposition"),
+ // When not asking for a specific benchmark mode,
+ // we'll analyse the results.
+ clEnumValN(exegesis::InstructionBenchmark::Unknown,
+ "analysis", "Analysis")));
+
+static cl::opt<unsigned>
NumRepetitions("num-repetitions",
- llvm::cl::desc("number of time to repeat the asm snippet"),
- llvm::cl::init(10000));
+ cl::desc("number of time to repeat the asm snippet"),
+ cl::init(10000));
-static llvm::cl::opt<bool> IgnoreInvalidSchedClass(
+static cl::opt<bool> IgnoreInvalidSchedClass(
"ignore-invalid-sched-class",
- llvm::cl::desc("ignore instructions that do not define a sched class"),
- llvm::cl::init(false));
+ cl::desc("ignore instructions that do not define a sched class"),
+ cl::init(false));
-static llvm::cl::opt<unsigned> AnalysisNumPoints(
+static cl::opt<unsigned> AnalysisNumPoints(
"analysis-numpoints",
- llvm::cl::desc("minimum number of points in an analysis cluster"),
- llvm::cl::init(3));
+ cl::desc("minimum number of points in an analysis cluster"), cl::init(3));
-static llvm::cl::opt<float>
+static cl::opt<float>
AnalysisEpsilon("analysis-epsilon",
- llvm::cl::desc("dbscan epsilon for analysis clustering"),
- llvm::cl::init(0.1));
+ cl::desc("dbscan epsilon for analysis clustering"),
+ cl::init(0.1));
-static llvm::cl::opt<std::string>
- AnalysisClustersOutputFile("analysis-clusters-output-file",
- llvm::cl::desc(""), llvm::cl::init("-"));
-static llvm::cl::opt<std::string>
+static cl::opt<std::string>
+ AnalysisClustersOutputFile("analysis-clusters-output-file", cl::desc(""),
+ cl::init("-"));
+static cl::opt<std::string>
AnalysisInconsistenciesOutputFile("analysis-inconsistencies-output-file",
- llvm::cl::desc(""), llvm::cl::init("-"));
+ cl::desc(""), cl::init("-"));
+
+static cl::opt<std::string>
+ CpuName("mcpu",
+ cl::desc(
+ "cpu name to use for pfm counters, leave empty to autodetect"),
+ cl::init(""));
-namespace exegesis {
-static llvm::ExitOnError ExitOnErr;
+static ExitOnError ExitOnErr;
#ifdef LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET
void LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET();
#endif
-static unsigned GetOpcodeOrDie(const llvm::MCInstrInfo &MCInstrInfo) {
- if (OpcodeName.empty() && (OpcodeIndex == 0))
+// Checks that only one of OpcodeNames, OpcodeIndex or SnippetsFile is provided,
+// and returns the opcode indices or {} if snippets should be read from
+// `SnippetsFile`.
+static std::vector<unsigned>
+getOpcodesOrDie(const llvm::MCInstrInfo &MCInstrInfo) {
+ const size_t NumSetFlags = (OpcodeNames.empty() ? 0 : 1) +
+ (OpcodeIndex == 0 ? 0 : 1) +
+ (SnippetsFile.empty() ? 0 : 1);
+ if (NumSetFlags != 1)
llvm::report_fatal_error(
- "please provide one and only one of 'opcode-index' or 'opcode-name'");
+ "please provide one and only one of 'opcode-index', 'opcode-name' or "
+ "'snippets-file'");
+ if (!SnippetsFile.empty())
+ return {};
if (OpcodeIndex > 0)
- return OpcodeIndex;
+ return {static_cast<unsigned>(OpcodeIndex)};
+ if (OpcodeIndex < 0) {
+ std::vector<unsigned> Result;
+ for (unsigned I = 1, E = MCInstrInfo.getNumOpcodes(); I < E; ++I)
+ Result.push_back(I);
+ return Result;
+ }
// Resolve opcode name -> opcode.
- for (unsigned I = 0, E = MCInstrInfo.getNumOpcodes(); I < E; ++I)
- if (MCInstrInfo.getName(I) == OpcodeName)
- return I;
- llvm::report_fatal_error(llvm::Twine("unknown opcode ").concat(OpcodeName));
+ const auto ResolveName =
+ [&MCInstrInfo](llvm::StringRef OpcodeName) -> unsigned {
+ for (unsigned I = 1, E = MCInstrInfo.getNumOpcodes(); I < E; ++I)
+ if (MCInstrInfo.getName(I) == OpcodeName)
+ return I;
+ return 0u;
+ };
+ llvm::SmallVector<llvm::StringRef, 2> Pieces;
+ llvm::StringRef(OpcodeNames.getValue())
+ .split(Pieces, ",", /* MaxSplit */ -1, /* KeepEmpty */ false);
+ std::vector<unsigned> Result;
+ for (const llvm::StringRef OpcodeName : Pieces) {
+ if (unsigned Opcode = ResolveName(OpcodeName))
+ Result.push_back(Opcode);
+ else
+ llvm::report_fatal_error(
+ llvm::Twine("unknown opcode ").concat(OpcodeName));
+ }
+ return Result;
}
-static BenchmarkResultContext
-getBenchmarkResultContext(const LLVMState &State) {
- BenchmarkResultContext Ctx;
+// Generates code snippets for opcode `Opcode`.
+static llvm::Expected<std::vector<BenchmarkCode>>
+generateSnippets(const LLVMState &State, unsigned Opcode) {
+ const Instruction &Instr = State.getIC().getInstr(Opcode);
+ const llvm::MCInstrDesc &InstrDesc = *Instr.Description;
+ // Ignore instructions that we cannot run.
+ if (InstrDesc.isPseudo())
+ return llvm::make_error<BenchmarkFailure>("Unsupported opcode: isPseudo");
+ if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch())
+ return llvm::make_error<BenchmarkFailure>(
+ "Unsupported opcode: isBranch/isIndirectBranch");
+ if (InstrDesc.isCall() || InstrDesc.isReturn())
+ return llvm::make_error<BenchmarkFailure>(
+ "Unsupported opcode: isCall/isReturn");
+
+ const std::unique_ptr<SnippetGenerator> Generator =
+ State.getExegesisTarget().createSnippetGenerator(BenchmarkMode, State);
+ if (!Generator)
+ llvm::report_fatal_error("cannot create snippet generator");
+ return Generator->generateConfigurations(Instr);
+}
+
+namespace {
+
+// An MCStreamer that reads a BenchmarkCode definition from a file.
+// The BenchmarkCode definition is just an asm file, with additional comments to
+// specify which registers should be defined or are live on entry.
+class BenchmarkCodeStreamer : public llvm::MCStreamer,
+ public llvm::AsmCommentConsumer {
+public:
+ explicit BenchmarkCodeStreamer(llvm::MCContext *Context,
+ const llvm::MCRegisterInfo *TheRegInfo,
+ BenchmarkCode *Result)
+ : llvm::MCStreamer(*Context), RegInfo(TheRegInfo), Result(Result) {}
+
+ // Implementation of the llvm::MCStreamer interface. We only care about
+ // instructions.
+ void EmitInstruction(const llvm::MCInst &Instruction,
+ const llvm::MCSubtargetInfo &STI,
+ bool PrintSchedInfo) override {
+ Result->Instructions.push_back(Instruction);
+ }
+
+ // Implementation of the llvm::AsmCommentConsumer.
+ void HandleComment(llvm::SMLoc Loc, llvm::StringRef CommentText) override {
+ CommentText = CommentText.trim();
+ if (!CommentText.consume_front("LLVM-EXEGESIS-"))
+ return;
+ if (CommentText.consume_front("DEFREG")) {
+ // LLVM-EXEGESIS-DEFREF <reg> <hex_value>
+ RegisterValue RegVal;
+ llvm::SmallVector<llvm::StringRef, 2> Parts;
+ CommentText.split(Parts, ' ', /*unlimited splits*/ -1,
+ /*do not keep empty strings*/ false);
+ if (Parts.size() != 2) {
+ llvm::errs() << "invalid comment 'LLVM-EXEGESIS-DEFREG " << CommentText
+ << "\n";
+ ++InvalidComments;
+ }
+ if (!(RegVal.Register = findRegisterByName(Parts[0].trim()))) {
+ llvm::errs() << "unknown register in 'LLVM-EXEGESIS-DEFREG "
+ << CommentText << "\n";
+ ++InvalidComments;
+ return;
+ }
+ const llvm::StringRef HexValue = Parts[1].trim();
+ RegVal.Value = llvm::APInt(
+ /* each hex digit is 4 bits */ HexValue.size() * 4, HexValue, 16);
+ Result->RegisterInitialValues.push_back(std::move(RegVal));
+ return;
+ }
+ if (CommentText.consume_front("LIVEIN")) {
+ // LLVM-EXEGESIS-LIVEIN <reg>
+ if (unsigned Reg = findRegisterByName(CommentText.ltrim()))
+ Result->LiveIns.push_back(Reg);
+ else {
+ llvm::errs() << "unknown register in 'LLVM-EXEGESIS-LIVEIN "
+ << CommentText << "\n";
+ ++InvalidComments;
+ }
+ return;
+ }
+ }
- const llvm::MCInstrInfo &InstrInfo = State.getInstrInfo();
- for (unsigned E = InstrInfo.getNumOpcodes(), I = 0; I < E; ++I)
- Ctx.addInstrEntry(I, InstrInfo.getName(I).data());
+ unsigned numInvalidComments() const { return InvalidComments; }
- const llvm::MCRegisterInfo &RegInfo = State.getRegInfo();
- for (unsigned E = RegInfo.getNumRegs(), I = 0; I < E; ++I)
- Ctx.addRegEntry(I, RegInfo.getName(I));
+private:
+ // We only care about instructions, we don't implement this part of the API.
+ void EmitCommonSymbol(llvm::MCSymbol *Symbol, uint64_t Size,
+ unsigned ByteAlignment) override {}
+ bool EmitSymbolAttribute(llvm::MCSymbol *Symbol,
+ llvm::MCSymbolAttr Attribute) override {
+ return false;
+ }
+ void EmitValueToAlignment(unsigned ByteAlignment, int64_t Value,
+ unsigned ValueSize,
+ unsigned MaxBytesToEmit) override {}
+ void EmitZerofill(llvm::MCSection *Section, llvm::MCSymbol *Symbol,
+ uint64_t Size, unsigned ByteAlignment,
+ llvm::SMLoc Loc) override {}
+
+ unsigned findRegisterByName(const llvm::StringRef RegName) const {
+ // FIXME: Can we do better than this ?
+ for (unsigned I = 0, E = RegInfo->getNumRegs(); I < E; ++I) {
+ if (RegName == RegInfo->getName(I))
+ return I;
+ }
+ llvm::errs() << "'" << RegName
+ << "' is not a valid register name for the target\n";
+ return 0;
+ }
- return Ctx;
+ const llvm::MCRegisterInfo *const RegInfo;
+ BenchmarkCode *const Result;
+ unsigned InvalidComments = 0;
+};
+
+} // namespace
+
+// Reads code snippets from file `Filename`.
+static llvm::Expected<std::vector<BenchmarkCode>>
+readSnippets(const LLVMState &State, llvm::StringRef Filename) {
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferPtr =
+ llvm::MemoryBuffer::getFileOrSTDIN(Filename);
+ if (std::error_code EC = BufferPtr.getError()) {
+ return llvm::make_error<BenchmarkFailure>(
+ "cannot read snippet: " + Filename + ": " + EC.message());
+ }
+ llvm::SourceMgr SM;
+ SM.AddNewSourceBuffer(std::move(BufferPtr.get()), llvm::SMLoc());
+
+ BenchmarkCode Result;
+
+ llvm::MCObjectFileInfo ObjectFileInfo;
+ const llvm::TargetMachine &TM = State.getTargetMachine();
+ llvm::MCContext Context(TM.getMCAsmInfo(), TM.getMCRegisterInfo(),
+ &ObjectFileInfo);
+ ObjectFileInfo.InitMCObjectFileInfo(TM.getTargetTriple(), /*PIC*/ false,
+ Context);
+ BenchmarkCodeStreamer Streamer(&Context, TM.getMCRegisterInfo(), &Result);
+ const std::unique_ptr<llvm::MCAsmParser> AsmParser(
+ llvm::createMCAsmParser(SM, Context, Streamer, *TM.getMCAsmInfo()));
+ if (!AsmParser)
+ return llvm::make_error<BenchmarkFailure>("cannot create asm parser");
+ AsmParser->getLexer().setCommentConsumer(&Streamer);
+
+ const std::unique_ptr<llvm::MCTargetAsmParser> TargetAsmParser(
+ TM.getTarget().createMCAsmParser(*TM.getMCSubtargetInfo(), *AsmParser,
+ *TM.getMCInstrInfo(),
+ llvm::MCTargetOptions()));
+
+ if (!TargetAsmParser)
+ return llvm::make_error<BenchmarkFailure>(
+ "cannot create target asm parser");
+ AsmParser->setTargetParser(*TargetAsmParser);
+
+ if (AsmParser->Run(false))
+ return llvm::make_error<BenchmarkFailure>("cannot parse asm file");
+ if (Streamer.numInvalidComments())
+ return llvm::make_error<BenchmarkFailure>(
+ llvm::Twine("found ")
+ .concat(llvm::Twine(Streamer.numInvalidComments()))
+ .concat(" invalid LLVM-EXEGESIS comments"));
+ return std::vector<BenchmarkCode>{std::move(Result)};
}
void benchmarkMain() {
@@ -125,19 +323,37 @@ void benchmarkMain() {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
+ llvm::InitializeNativeTargetAsmParser();
#ifdef LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET
LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET();
#endif
- const LLVMState State;
- const auto Opcode = GetOpcodeOrDie(State.getInstrInfo());
-
- // Ignore instructions without a sched class if -ignore-invalid-sched-class is
- // passed.
- if (IgnoreInvalidSchedClass &&
- State.getInstrInfo().get(Opcode).getSchedClass() == 0) {
- llvm::errs() << "ignoring instruction without sched class\n";
- return;
+ const LLVMState State(CpuName);
+ const auto Opcodes = getOpcodesOrDie(State.getInstrInfo());
+
+ std::vector<BenchmarkCode> Configurations;
+ if (!Opcodes.empty()) {
+ for (const unsigned Opcode : Opcodes) {
+ // Ignore instructions without a sched class if
+ // -ignore-invalid-sched-class is passed.
+ if (IgnoreInvalidSchedClass &&
+ State.getInstrInfo().get(Opcode).getSchedClass() == 0) {
+ llvm::errs() << State.getInstrInfo().getName(Opcode)
+ << ": ignoring instruction without sched class\n";
+ continue;
+ }
+ auto ConfigsForInstr = generateSnippets(State, Opcode);
+ if (!ConfigsForInstr) {
+ llvm::logAllUnhandledErrors(
+ ConfigsForInstr.takeError(), llvm::errs(),
+ llvm::Twine(State.getInstrInfo().getName(Opcode)).concat(": "));
+ continue;
+ }
+ std::move(ConfigsForInstr->begin(), ConfigsForInstr->end(),
+ std::back_inserter(Configurations));
+ }
+ } else {
+ Configurations = ExitOnErr(readSnippets(State, SnippetsFile));
}
const std::unique_ptr<BenchmarkRunner> Runner =
@@ -153,12 +369,11 @@ void benchmarkMain() {
if (BenchmarkFile.empty())
BenchmarkFile = "-";
- const BenchmarkResultContext Context = getBenchmarkResultContext(State);
- std::vector<InstructionBenchmark> Results =
- ExitOnErr(Runner->run(Opcode, NumRepetitions));
- for (InstructionBenchmark &Result : Results)
- ExitOnErr(Result.writeYaml(Context, BenchmarkFile));
-
+ for (const BenchmarkCode &Conf : Configurations) {
+ InstructionBenchmark Result =
+ Runner->runConfiguration(Conf, NumRepetitions);
+ ExitOnErr(Result.writeYaml(State, BenchmarkFile));
+ }
exegesis::pfm::pfmTerminate();
}
@@ -191,10 +406,9 @@ static void analysisMain() {
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetDisassembler();
// Read benchmarks.
- const LLVMState State;
+ const LLVMState State("");
const std::vector<InstructionBenchmark> Points =
- ExitOnErr(InstructionBenchmark::readYamls(
- getBenchmarkResultContext(State), BenchmarkFile));
+ ExitOnErr(InstructionBenchmark::readYamls(State, BenchmarkFile));
llvm::outs() << "Parsed " << Points.size() << " benchmark points\n";
if (Points.empty()) {
llvm::errs() << "no benchmarks to analyze\n";
@@ -223,9 +437,11 @@ static void analysisMain() {
}
} // namespace exegesis
+} // namespace llvm
int main(int Argc, char **Argv) {
- llvm::cl::ParseCommandLineOptions(Argc, Argv, "");
+ using namespace llvm;
+ cl::ParseCommandLineOptions(Argc, Argv, "");
exegesis::ExitOnErr.setExitCodeMapper([](const llvm::Error &Err) {
if (Err.isA<llvm::StringError>())
@@ -233,7 +449,7 @@ int main(int Argc, char **Argv) {
return EXIT_FAILURE;
});
- if (BenchmarkMode == exegesis::InstructionBenchmark::Unknown) {
+ if (exegesis::BenchmarkMode == exegesis::InstructionBenchmark::Unknown) {
exegesis::analysisMain();
} else {
exegesis::benchmarkMain();
diff --git a/tools/llvm-go/llvm-go.go b/tools/llvm-go/llvm-go.go
index 4d65de623462..8215483b8332 100644
--- a/tools/llvm-go/llvm-go.go
+++ b/tools/llvm-go/llvm-go.go
@@ -50,6 +50,7 @@ var components = []string{
"bitwriter",
"codegen",
"core",
+ "coroutines",
"debuginfodwarf",
"executionengine",
"instrumentation",
diff --git a/tools/llvm-demangle-fuzzer/CMakeLists.txt b/tools/llvm-itanium-demangle-fuzzer/CMakeLists.txt
index 0fe711cdb16c..07f02a35b203 100644
--- a/tools/llvm-demangle-fuzzer/CMakeLists.txt
+++ b/tools/llvm-itanium-demangle-fuzzer/CMakeLists.txt
@@ -4,7 +4,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_llvm_fuzzer(llvm-demangle-fuzzer
- llvm-demangle-fuzzer.cpp
+add_llvm_fuzzer(llvm-itanium-demangle-fuzzer
+ llvm-itanium-demangle-fuzzer.cpp
DUMMY_MAIN DummyDemanglerFuzzer.cpp
)
diff --git a/tools/llvm-demangle-fuzzer/DummyDemanglerFuzzer.cpp b/tools/llvm-itanium-demangle-fuzzer/DummyDemanglerFuzzer.cpp
index a2bf9f1b807e..a2bf9f1b807e 100644
--- a/tools/llvm-demangle-fuzzer/DummyDemanglerFuzzer.cpp
+++ b/tools/llvm-itanium-demangle-fuzzer/DummyDemanglerFuzzer.cpp
diff --git a/tools/llvm-demangle-fuzzer/llvm-demangle-fuzzer.cpp b/tools/llvm-itanium-demangle-fuzzer/llvm-itanium-demangle-fuzzer.cpp
index 07c290a0be5c..07c290a0be5c 100644
--- a/tools/llvm-demangle-fuzzer/llvm-demangle-fuzzer.cpp
+++ b/tools/llvm-itanium-demangle-fuzzer/llvm-itanium-demangle-fuzzer.cpp
diff --git a/tools/llvm-lto/llvm-lto.cpp b/tools/llvm-lto/llvm-lto.cpp
index 75668a9dd8b6..b6facc919b51 100644
--- a/tools/llvm-lto/llvm-lto.cpp
+++ b/tools/llvm-lto/llvm-lto.cpp
@@ -158,7 +158,7 @@ static cl::opt<int>
ThinLTOCachePruningInterval("thinlto-cache-pruning-interval",
cl::init(1200), cl::desc("Set ThinLTO cache pruning interval."));
-static cl::opt<int>
+static cl::opt<unsigned long long>
ThinLTOCacheMaxSizeBytes("thinlto-cache-max-size-bytes",
cl::desc("Set ThinLTO cache pruning directory maximum size in bytes."));
@@ -166,6 +166,10 @@ static cl::opt<int>
ThinLTOCacheMaxSizeFiles("thinlto-cache-max-size-files", cl::init(1000000),
cl::desc("Set ThinLTO cache pruning directory maximum number of files."));
+static cl::opt<unsigned>
+ ThinLTOCacheEntryExpiration("thinlto-cache-entry-expiration", cl::init(604800) /* 1w */,
+ cl::desc("Set ThinLTO cache entry expiration time."));
+
static cl::opt<std::string> ThinLTOSaveTempsPrefix(
"thinlto-save-temps",
cl::desc("Save ThinLTO temp files using filenames created by adding "
@@ -481,6 +485,7 @@ public:
ThinGenerator.setTargetOptions(Options);
ThinGenerator.setCacheDir(ThinLTOCacheDir);
ThinGenerator.setCachePruningInterval(ThinLTOCachePruningInterval);
+ ThinGenerator.setCacheEntryExpiration(ThinLTOCacheEntryExpiration);
ThinGenerator.setCacheMaxSizeFiles(ThinLTOCacheMaxSizeFiles);
ThinGenerator.setCacheMaxSizeBytes(ThinLTOCacheMaxSizeBytes);
ThinGenerator.setFreestanding(EnableFreestanding);
@@ -557,11 +562,14 @@ private:
auto Index = loadCombinedIndex();
for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto TheModule = loadModule(Filename, Ctx);
+
// Build a map of module to the GUIDs and summary objects that should
// be written to its index.
std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex;
- ThinLTOCodeGenerator::gatherImportedSummariesForModule(
- Filename, *Index, ModuleToSummariesForIndex);
+ ThinGenerator.gatherImportedSummariesForModule(*TheModule, *Index,
+ ModuleToSummariesForIndex);
std::string OutputName = OutputFilename;
if (OutputName.empty()) {
@@ -589,12 +597,14 @@ private:
auto Index = loadCombinedIndex();
for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto TheModule = loadModule(Filename, Ctx);
std::string OutputName = OutputFilename;
if (OutputName.empty()) {
OutputName = Filename + ".imports";
}
OutputName = getThinLTOOutputFile(OutputName, OldPrefix, NewPrefix);
- ThinLTOCodeGenerator::emitImports(Filename, OutputName, *Index);
+ ThinGenerator.emitImports(*TheModule, OutputName, *Index);
}
}
diff --git a/tools/llvm-lto2/llvm-lto2.cpp b/tools/llvm-lto2/llvm-lto2.cpp
index 442973f90209..26426367e252 100644
--- a/tools/llvm-lto2/llvm-lto2.cpp
+++ b/tools/llvm-lto2/llvm-lto2.cpp
@@ -23,6 +23,7 @@
#include "llvm/LTO/LTO.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Threading.h"
@@ -388,6 +389,7 @@ static int dumpSymtab(int argc, char **argv) {
}
int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
diff --git a/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp b/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp
index 8aad6ab67423..8731cf60fca8 100644
--- a/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp
+++ b/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp
@@ -18,6 +18,7 @@
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCParser/AsmLexer.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCRegisterInfo.h"
@@ -230,9 +231,9 @@ int AssembleOneInput(const uint8_t *Data, size_t Size) {
MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx);
MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions);
Str.reset(TheTarget->createMCObjectStreamer(
- TheTriple, Ctx, std::unique_ptr<MCAsmBackend>(MAB), *OS,
- std::unique_ptr<MCCodeEmitter>(CE), *STI, MCOptions.MCRelaxAll,
- MCOptions.MCIncrementalLinkerCompatible,
+ TheTriple, Ctx, std::unique_ptr<MCAsmBackend>(MAB),
+ MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(CE), *STI,
+ MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible,
/*DWARFMustBeAtTheEnd*/ false));
}
const int Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, *Str, *MAI, *STI,
diff --git a/tools/llvm-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp
index f494d02f3bca..c0976502f545 100644
--- a/tools/llvm-mc/llvm-mc.cpp
+++ b/tools/llvm-mc/llvm-mc.cpp
@@ -164,6 +164,10 @@ MainFileName("main-file-name",
static cl::opt<bool> SaveTempLabels("save-temp-labels",
cl::desc("Don't discard temporary labels"));
+static cl::opt<bool> LexMasmIntegers(
+ "masm-integers",
+ cl::desc("Enable binary and hex masm integers (0b110 and 0ABCh)"));
+
static cl::opt<bool> NoExecStack("no-exec-stack",
cl::desc("File doesn't need an exec stack"));
@@ -293,6 +297,7 @@ static int AssembleInput(const char *ProgName, const Target *TheTarget,
return SymbolResult;
Parser->setShowParsedOperands(ShowInstOperands);
Parser->setTargetParser(*TAP);
+ Parser->getLexer().setLexMasmIntegers(LexMasmIntegers);
int Res = Parser->Run(NoInitialTextSection);
@@ -313,7 +318,6 @@ int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv, "llvm machine code playground\n");
MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags();
- TripleName = Triple::normalize(TripleName);
setDwarfDebugFlags(argc, argv);
setDwarfDebugProducer();
diff --git a/tools/llvm-mca/CMakeLists.txt b/tools/llvm-mca/CMakeLists.txt
index bb657b8d19fe..1fceb08c1ca3 100644
--- a/tools/llvm-mca/CMakeLists.txt
+++ b/tools/llvm-mca/CMakeLists.txt
@@ -1,42 +1,31 @@
+include_directories(include)
+
set(LLVM_LINK_COMPONENTS
AllTargetsAsmPrinters
AllTargetsAsmParsers
AllTargetsDescs
AllTargetsDisassemblers
AllTargetsInfos
+ MCA
MC
MCParser
Support
)
add_llvm_tool(llvm-mca
- CodeRegion.cpp
- Context.cpp
- DispatchStage.cpp
- DispatchStatistics.cpp
- ExecuteStage.cpp
- FetchStage.cpp
- HWEventListener.cpp
- HardwareUnit.cpp
- InstrBuilder.cpp
- Instruction.cpp
- InstructionInfoView.cpp
- InstructionTables.cpp
- LSUnit.cpp
llvm-mca.cpp
- Pipeline.cpp
+ CodeRegion.cpp
+ CodeRegionGenerator.cpp
PipelinePrinter.cpp
- RegisterFile.cpp
- RegisterFileStatistics.cpp
- ResourcePressureView.cpp
- RetireControlUnit.cpp
- RetireControlUnitStatistics.cpp
- RetireStage.cpp
- Scheduler.cpp
- SchedulerStatistics.cpp
- Stage.cpp
- Support.cpp
- SummaryView.cpp
- TimelineView.cpp
- View.cpp
+ Views/DispatchStatistics.cpp
+ Views/InstructionInfoView.cpp
+ Views/RegisterFileStatistics.cpp
+ Views/ResourcePressureView.cpp
+ Views/RetireControlUnitStatistics.cpp
+ Views/SchedulerStatistics.cpp
+ Views/SummaryView.cpp
+ Views/TimelineView.cpp
+ Views/View.cpp
)
+
+set(LLVM_MCA_SOURCE_DIR ${CURRENT_SOURCE_DIR})
diff --git a/tools/llvm-mca/CodeRegion.cpp b/tools/llvm-mca/CodeRegion.cpp
index 896865996504..29a27c50c171 100644
--- a/tools/llvm-mca/CodeRegion.cpp
+++ b/tools/llvm-mca/CodeRegion.cpp
@@ -14,11 +14,10 @@
#include "CodeRegion.h"
-using namespace llvm;
-
+namespace llvm {
namespace mca {
-bool CodeRegion::isLocInRange(SMLoc Loc) const {
+bool CodeRegion::isLocInRange(llvm::SMLoc Loc) const {
if (RangeEnd.isValid() && Loc.getPointer() > RangeEnd.getPointer())
return false;
if (RangeStart.isValid() && Loc.getPointer() < RangeStart.getPointer())
@@ -26,11 +25,11 @@ bool CodeRegion::isLocInRange(SMLoc Loc) const {
return true;
}
-void CodeRegions::beginRegion(StringRef Description, SMLoc Loc) {
+void CodeRegions::beginRegion(llvm::StringRef Description, llvm::SMLoc Loc) {
assert(!Regions.empty() && "Missing Default region");
const CodeRegion &CurrentRegion = *Regions.back();
if (CurrentRegion.startLoc().isValid() && !CurrentRegion.endLoc().isValid()) {
- SM.PrintMessage(Loc, SourceMgr::DK_Warning,
+ SM.PrintMessage(Loc, llvm::SourceMgr::DK_Warning,
"Ignoring invalid region start");
return;
}
@@ -41,26 +40,28 @@ void CodeRegions::beginRegion(StringRef Description, SMLoc Loc) {
addRegion(Description, Loc);
}
-void CodeRegions::endRegion(SMLoc Loc) {
+void CodeRegions::endRegion(llvm::SMLoc Loc) {
assert(!Regions.empty() && "Missing Default region");
CodeRegion &CurrentRegion = *Regions.back();
if (CurrentRegion.endLoc().isValid()) {
- SM.PrintMessage(Loc, SourceMgr::DK_Warning, "Ignoring invalid region end");
+ SM.PrintMessage(Loc, llvm::SourceMgr::DK_Warning,
+ "Ignoring invalid region end");
return;
}
CurrentRegion.setEndLocation(Loc);
}
-void CodeRegions::addInstruction(std::unique_ptr<const MCInst> Instruction) {
- const SMLoc &Loc = Instruction->getLoc();
+void CodeRegions::addInstruction(const llvm::MCInst &Instruction) {
+ const llvm::SMLoc &Loc = Instruction.getLoc();
const auto It =
std::find_if(Regions.rbegin(), Regions.rend(),
[Loc](const std::unique_ptr<CodeRegion> &Region) {
return Region->isLocInRange(Loc);
});
if (It != Regions.rend())
- (*It)->addInstruction(std::move(Instruction));
+ (*It)->addInstruction(Instruction);
}
} // namespace mca
+} // namespace llvm
diff --git a/tools/llvm-mca/CodeRegion.h b/tools/llvm-mca/CodeRegion.h
index 7f0025e4884c..867aa18bb4fe 100644
--- a/tools/llvm-mca/CodeRegion.h
+++ b/tools/llvm-mca/CodeRegion.h
@@ -34,12 +34,14 @@
#ifndef LLVM_TOOLS_LLVM_MCA_CODEREGION_H
#define LLVM_TOOLS_LLVM_MCA_CODEREGION_H
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCInst.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/SourceMgr.h"
#include <vector>
+namespace llvm {
namespace mca {
/// A region of assembly code.
@@ -49,7 +51,7 @@ class CodeRegion {
// An optional descriptor for this region.
llvm::StringRef Description;
// Instructions that form this region.
- std::vector<std::unique_ptr<const llvm::MCInst>> Instructions;
+ std::vector<llvm::MCInst> Instructions;
// Source location range.
llvm::SMLoc RangeStart;
llvm::SMLoc RangeEnd;
@@ -61,8 +63,8 @@ public:
CodeRegion(llvm::StringRef Desc, llvm::SMLoc Start)
: Description(Desc), RangeStart(Start), RangeEnd() {}
- void addInstruction(std::unique_ptr<const llvm::MCInst> Instruction) {
- Instructions.emplace_back(std::move(Instruction));
+ void addInstruction(const llvm::MCInst &Instruction) {
+ Instructions.emplace_back(Instruction);
}
llvm::SMLoc startLoc() const { return RangeStart; }
@@ -72,10 +74,7 @@ public:
bool empty() const { return Instructions.empty(); }
bool isLocInRange(llvm::SMLoc Loc) const;
- const std::vector<std::unique_ptr<const llvm::MCInst>> &
- getInstructions() const {
- return Instructions;
- }
+ llvm::ArrayRef<llvm::MCInst> getInstructions() const { return Instructions; }
llvm::StringRef getDescription() const { return Description; }
};
@@ -106,26 +105,26 @@ public:
void beginRegion(llvm::StringRef Description, llvm::SMLoc Loc);
void endRegion(llvm::SMLoc Loc);
- void addInstruction(std::unique_ptr<const llvm::MCInst> Instruction);
+ void addInstruction(const llvm::MCInst &Instruction);
+ llvm::SourceMgr &getSourceMgr() const { return SM; }
CodeRegions(llvm::SourceMgr &S) : SM(S) {
// Create a default region for the input code sequence.
addRegion("Default", llvm::SMLoc());
}
- const std::vector<std::unique_ptr<const llvm::MCInst>> &
- getInstructionSequence(unsigned Idx) const {
+ llvm::ArrayRef<llvm::MCInst> getInstructionSequence(unsigned Idx) const {
return Regions[Idx]->getInstructions();
}
bool empty() const {
- return std::all_of(Regions.begin(), Regions.end(),
- [](const std::unique_ptr<CodeRegion> &Region) {
- return Region->empty();
- });
+ return llvm::all_of(Regions, [](const std::unique_ptr<CodeRegion> &Region) {
+ return Region->empty();
+ });
}
};
} // namespace mca
+} // namespace llvm
#endif
diff --git a/tools/llvm-mca/CodeRegionGenerator.cpp b/tools/llvm-mca/CodeRegionGenerator.cpp
new file mode 100644
index 000000000000..5bd37adeeae9
--- /dev/null
+++ b/tools/llvm-mca/CodeRegionGenerator.cpp
@@ -0,0 +1,137 @@
+//===----------------------- CodeRegionGenerator.cpp ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines classes responsible for generating llvm-mca
+/// CodeRegions from various types of input. llvm-mca only analyzes CodeRegions,
+/// so the classes here provide the input-to-CodeRegions translation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CodeRegionGenerator.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/MC/MCParser/MCTargetAsmParser.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/SMLoc.h"
+#include <memory>
+
+namespace llvm {
+namespace mca {
+
+// This virtual dtor serves as the anchor for the CodeRegionGenerator class.
+CodeRegionGenerator::~CodeRegionGenerator() {}
+
+// A comment consumer that parses strings. The only valid tokens are strings.
+class MCACommentConsumer : public AsmCommentConsumer {
+public:
+ CodeRegions &Regions;
+
+ MCACommentConsumer(CodeRegions &R) : Regions(R) {}
+ void HandleComment(SMLoc Loc, StringRef CommentText) override;
+};
+
+// This class provides the callbacks that occur when parsing input assembly.
+class MCStreamerWrapper final : public MCStreamer {
+ CodeRegions &Regions;
+
+public:
+ MCStreamerWrapper(MCContext &Context, mca::CodeRegions &R)
+ : MCStreamer(Context), Regions(R) {}
+
+ // We only want to intercept the emission of new instructions.
+ virtual void EmitInstruction(const MCInst &Inst,
+ const MCSubtargetInfo & /* unused */,
+ bool /* unused */) override {
+ Regions.addInstruction(Inst);
+ }
+
+ bool EmitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
+ return true;
+ }
+
+ void EmitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
+ unsigned ByteAlignment) override {}
+ void EmitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
+ uint64_t Size = 0, unsigned ByteAlignment = 0,
+ SMLoc Loc = SMLoc()) override {}
+ void EmitGPRel32Value(const MCExpr *Value) override {}
+ void BeginCOFFSymbolDef(const MCSymbol *Symbol) override {}
+ void EmitCOFFSymbolStorageClass(int StorageClass) override {}
+ void EmitCOFFSymbolType(int Type) override {}
+ void EndCOFFSymbolDef() override {}
+
+ ArrayRef<MCInst> GetInstructionSequence(unsigned Index) const {
+ return Regions.getInstructionSequence(Index);
+ }
+};
+
+void MCACommentConsumer::HandleComment(SMLoc Loc, StringRef CommentText) {
+ // Skip empty comments.
+ StringRef Comment(CommentText);
+ if (Comment.empty())
+ return;
+
+ // Skip spaces and tabs.
+ unsigned Position = Comment.find_first_not_of(" \t");
+ if (Position >= Comment.size())
+ // We reached the end of the comment. Bail out.
+ return;
+
+ Comment = Comment.drop_front(Position);
+ if (Comment.consume_front("LLVM-MCA-END")) {
+ Regions.endRegion(Loc);
+ return;
+ }
+
+ // Try to parse the LLVM-MCA-BEGIN comment.
+ if (!Comment.consume_front("LLVM-MCA-BEGIN"))
+ return;
+
+ // Skip spaces and tabs.
+ Position = Comment.find_first_not_of(" \t");
+ if (Position < Comment.size())
+ Comment = Comment.drop_front(Position);
+ // Use the rest of the string as a descriptor for this code snippet.
+ Regions.beginRegion(Comment, Loc);
+}
+
+Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions() {
+ MCTargetOptions Opts;
+ Opts.PreserveAsmComments = false;
+ MCStreamerWrapper Str(Ctx, Regions);
+
+ // Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM
+ // comments.
+ std::unique_ptr<MCAsmParser> Parser(
+ createMCAsmParser(Regions.getSourceMgr(), Ctx, Str, MAI));
+ MCAsmLexer &Lexer = Parser->getLexer();
+ MCACommentConsumer CC(Regions);
+ Lexer.setCommentConsumer(&CC);
+
+ // Create a target-specific parser and perform the parse.
+ std::unique_ptr<MCTargetAsmParser> TAP(
+ TheTarget.createMCAsmParser(STI, *Parser, MCII, Opts));
+ if (!TAP)
+ return make_error<StringError>(
+ "This target does not support assembly parsing.",
+ inconvertibleErrorCode());
+ Parser->setTargetParser(*TAP);
+ Parser->Run(false);
+
+ // Get the assembler dialect from the input. llvm-mca will use this as the
+ // default dialect when printing reports.
+ AssemblerDialect = Parser->getAssemblerDialect();
+ return Regions;
+}
+
+} // namespace mca
+} // namespace llvm
diff --git a/tools/llvm-mca/CodeRegionGenerator.h b/tools/llvm-mca/CodeRegionGenerator.h
new file mode 100644
index 000000000000..892cafb92686
--- /dev/null
+++ b/tools/llvm-mca/CodeRegionGenerator.h
@@ -0,0 +1,70 @@
+//===----------------------- CodeRegionGenerator.h --------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file declares classes responsible for generating llvm-mca
+/// CodeRegions from various types of input. llvm-mca only analyzes CodeRegions,
+/// so the classes here provide the input-to-CodeRegions translation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_CODEREGION_GENERATOR_H
+#define LLVM_TOOLS_LLVM_MCA_CODEREGION_GENERATOR_H
+
+#include "CodeRegion.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetRegistry.h"
+#include <memory>
+
+namespace llvm {
+namespace mca {
+
+/// This class is responsible for parsing the input given to the llvm-mca
+/// driver, and converting that into a CodeRegions instance.
+class CodeRegionGenerator {
+protected:
+ CodeRegions Regions;
+ CodeRegionGenerator(const CodeRegionGenerator &) = delete;
+ CodeRegionGenerator &operator=(const CodeRegionGenerator &) = delete;
+
+public:
+ CodeRegionGenerator(SourceMgr &SM) : Regions(SM) {}
+ virtual ~CodeRegionGenerator();
+ virtual Expected<const CodeRegions &> parseCodeRegions() = 0;
+};
+
+/// This class is responsible for parsing input ASM and generating
+/// a CodeRegions instance.
+class AsmCodeRegionGenerator final : public CodeRegionGenerator {
+ const Target &TheTarget;
+ MCContext &Ctx;
+ const MCAsmInfo &MAI;
+ const MCSubtargetInfo &STI;
+ const MCInstrInfo &MCII;
+ unsigned AssemblerDialect; // This is set during parsing.
+
+public:
+ AsmCodeRegionGenerator(const Target &T, SourceMgr &SM, MCContext &C,
+ const MCAsmInfo &A, const MCSubtargetInfo &S,
+ const MCInstrInfo &I)
+ : CodeRegionGenerator(SM), TheTarget(T), Ctx(C), MAI(A), STI(S), MCII(I),
+ AssemblerDialect(0) {}
+
+ unsigned getAssemblerDialect() const { return AssemblerDialect; }
+ Expected<const CodeRegions &> parseCodeRegions() override;
+};
+
+} // namespace mca
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_MCA_CODEREGION_GENERATOR_H
diff --git a/tools/llvm-mca/Context.cpp b/tools/llvm-mca/Context.cpp
deleted file mode 100644
index 685714e64b92..000000000000
--- a/tools/llvm-mca/Context.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-//===---------------------------- Context.cpp -------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines a class for holding ownership of various simulated
-/// hardware units. A Context also provides a utility routine for constructing
-/// a default out-of-order pipeline with fetch, dispatch, execute, and retire
-/// stages).
-///
-//===----------------------------------------------------------------------===//
-
-#include "Context.h"
-#include "DispatchStage.h"
-#include "ExecuteStage.h"
-#include "FetchStage.h"
-#include "RegisterFile.h"
-#include "RetireControlUnit.h"
-#include "RetireStage.h"
-#include "Scheduler.h"
-
-namespace mca {
-
-using namespace llvm;
-
-std::unique_ptr<Pipeline>
-Context::createDefaultPipeline(const PipelineOptions &Opts, InstrBuilder &IB,
- SourceMgr &SrcMgr) {
- const MCSchedModel &SM = STI.getSchedModel();
-
- // Create the hardware units defining the backend.
- auto RCU = llvm::make_unique<RetireControlUnit>(SM);
- auto PRF = llvm::make_unique<RegisterFile>(SM, MRI, Opts.RegisterFileSize);
- auto HWS = llvm::make_unique<Scheduler>(
- SM, Opts.LoadQueueSize, Opts.StoreQueueSize, Opts.AssumeNoAlias);
-
- // Create the pipeline and its stages.
- auto P = llvm::make_unique<Pipeline>();
- auto F = llvm::make_unique<FetchStage>(IB, SrcMgr);
- auto D = llvm::make_unique<DispatchStage>(
- STI, MRI, Opts.RegisterFileSize, Opts.DispatchWidth, *RCU, *PRF, *HWS);
- auto R = llvm::make_unique<RetireStage>(*RCU, *PRF);
- auto E = llvm::make_unique<ExecuteStage>(*RCU, *HWS);
-
- // Add the hardware to the context.
- addHardwareUnit(std::move(RCU));
- addHardwareUnit(std::move(PRF));
- addHardwareUnit(std::move(HWS));
-
- // Build the pipeline.
- P->appendStage(std::move(F));
- P->appendStage(std::move(D));
- P->appendStage(std::move(R));
- P->appendStage(std::move(E));
- return P;
-}
-
-} // namespace mca
diff --git a/tools/llvm-mca/Context.h b/tools/llvm-mca/Context.h
deleted file mode 100644
index cf483fa7b37d..000000000000
--- a/tools/llvm-mca/Context.h
+++ /dev/null
@@ -1,68 +0,0 @@
-//===---------------------------- Context.h ---------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines a class for holding ownership of various simulated
-/// hardware units. A Context also provides a utility routine for constructing
-/// a default out-of-order pipeline with fetch, dispatch, execute, and retire
-/// stages).
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_CONTEXT_H
-#define LLVM_TOOLS_LLVM_MCA_CONTEXT_H
-#include "HardwareUnit.h"
-#include "InstrBuilder.h"
-#include "Pipeline.h"
-#include "SourceMgr.h"
-#include "llvm/MC/MCRegisterInfo.h"
-#include "llvm/MC/MCSchedule.h"
-#include "llvm/MC/MCSubtargetInfo.h"
-#include <memory>
-
-namespace mca {
-
-/// This is a convenience struct to hold the parameters necessary for creating
-/// the pre-built "default" out-of-order pipeline.
-struct PipelineOptions {
- PipelineOptions(unsigned DW, unsigned RFS, unsigned LQS, unsigned SQS,
- bool NoAlias)
- : DispatchWidth(DW), RegisterFileSize(RFS), LoadQueueSize(LQS),
- StoreQueueSize(SQS), AssumeNoAlias(NoAlias) {}
- unsigned DispatchWidth;
- unsigned RegisterFileSize;
- unsigned LoadQueueSize;
- unsigned StoreQueueSize;
- bool AssumeNoAlias;
-};
-
-class Context {
- llvm::SmallVector<std::unique_ptr<HardwareUnit>, 4> Hardware;
- const llvm::MCRegisterInfo &MRI;
- const llvm::MCSubtargetInfo &STI;
-
-public:
- Context(const llvm::MCRegisterInfo &R, const llvm::MCSubtargetInfo &S)
- : MRI(R), STI(S) {}
- Context(const Context &C) = delete;
- Context &operator=(const Context &C) = delete;
-
- void addHardwareUnit(std::unique_ptr<HardwareUnit> H) {
- Hardware.push_back(std::move(H));
- }
-
- /// Construct a basic pipeline for simulating an out-of-order pipeline.
- /// This pipeline consists of Fetch, Dispatch, Execute, and Retire stages.
- std::unique_ptr<Pipeline> createDefaultPipeline(const PipelineOptions &Opts,
- InstrBuilder &IB,
- SourceMgr &SrcMgr);
-};
-
-} // namespace mca
-#endif // LLVM_TOOLS_LLVM_MCA_CONTEXT_H
diff --git a/tools/llvm-mca/DispatchStage.cpp b/tools/llvm-mca/DispatchStage.cpp
deleted file mode 100644
index 1f508886c298..000000000000
--- a/tools/llvm-mca/DispatchStage.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-//===--------------------- DispatchStage.cpp --------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file models the dispatch component of an instruction pipeline.
-///
-/// The DispatchStage is responsible for updating instruction dependencies
-/// and communicating to the simulated instruction scheduler that an instruction
-/// is ready to be scheduled for execution.
-///
-//===----------------------------------------------------------------------===//
-
-#include "DispatchStage.h"
-#include "HWEventListener.h"
-#include "Scheduler.h"
-#include "llvm/Support/Debug.h"
-
-using namespace llvm;
-
-#define DEBUG_TYPE "llvm-mca"
-
-namespace mca {
-
-void DispatchStage::notifyInstructionDispatched(const InstRef &IR,
- ArrayRef<unsigned> UsedRegs) {
- LLVM_DEBUG(dbgs() << "[E] Instruction Dispatched: #" << IR << '\n');
- notifyEvent<HWInstructionEvent>(HWInstructionDispatchedEvent(IR, UsedRegs));
-}
-
-bool DispatchStage::checkPRF(const InstRef &IR) {
- SmallVector<unsigned, 4> RegDefs;
- for (const std::unique_ptr<WriteState> &RegDef :
- IR.getInstruction()->getDefs())
- RegDefs.emplace_back(RegDef->getRegisterID());
-
- const unsigned RegisterMask = PRF.isAvailable(RegDefs);
- // A mask with all zeroes means: register files are available.
- if (RegisterMask) {
- notifyEvent<HWStallEvent>(
- HWStallEvent(HWStallEvent::RegisterFileStall, IR));
- return false;
- }
-
- return true;
-}
-
-bool DispatchStage::checkRCU(const InstRef &IR) {
- const unsigned NumMicroOps = IR.getInstruction()->getDesc().NumMicroOps;
- if (RCU.isAvailable(NumMicroOps))
- return true;
- notifyEvent<HWStallEvent>(
- HWStallEvent(HWStallEvent::RetireControlUnitStall, IR));
- return false;
-}
-
-bool DispatchStage::checkScheduler(const InstRef &IR) {
- HWStallEvent::GenericEventType Event;
- const bool Ready = SC.canBeDispatched(IR, Event);
- if (!Ready)
- notifyEvent<HWStallEvent>(HWStallEvent(Event, IR));
- return Ready;
-}
-
-void DispatchStage::updateRAWDependencies(ReadState &RS,
- const MCSubtargetInfo &STI) {
- SmallVector<WriteRef, 4> DependentWrites;
-
- collectWrites(DependentWrites, RS.getRegisterID());
- RS.setDependentWrites(DependentWrites.size());
- // We know that this read depends on all the writes in DependentWrites.
- // For each write, check if we have ReadAdvance information, and use it
- // to figure out in how many cycles this read becomes available.
- const ReadDescriptor &RD = RS.getDescriptor();
- const MCSchedModel &SM = STI.getSchedModel();
- const MCSchedClassDesc *SC = SM.getSchedClassDesc(RD.SchedClassID);
- for (WriteRef &WR : DependentWrites) {
- WriteState &WS = *WR.getWriteState();
- unsigned WriteResID = WS.getWriteResourceID();
- int ReadAdvance = STI.getReadAdvanceCycles(SC, RD.UseIndex, WriteResID);
- WS.addUser(&RS, ReadAdvance);
- }
-}
-
-void DispatchStage::dispatch(InstRef IR) {
- assert(!CarryOver && "Cannot dispatch another instruction!");
- Instruction &IS = *IR.getInstruction();
- const InstrDesc &Desc = IS.getDesc();
- const unsigned NumMicroOps = Desc.NumMicroOps;
- if (NumMicroOps > DispatchWidth) {
- assert(AvailableEntries == DispatchWidth);
- AvailableEntries = 0;
- CarryOver = NumMicroOps - DispatchWidth;
- } else {
- assert(AvailableEntries >= NumMicroOps);
- AvailableEntries -= NumMicroOps;
- }
-
- // A dependency-breaking instruction doesn't have to wait on the register
- // input operands, and it is often optimized at register renaming stage.
- // Update RAW dependencies if this instruction is not a dependency-breaking
- // instruction. A dependency-breaking instruction is a zero-latency
- // instruction that doesn't consume hardware resources.
- // An example of dependency-breaking instruction on X86 is a zero-idiom XOR.
- bool IsDependencyBreaking = IS.isDependencyBreaking();
- for (std::unique_ptr<ReadState> &RS : IS.getUses())
- if (RS->isImplicitRead() || !IsDependencyBreaking)
- updateRAWDependencies(*RS, STI);
-
- // By default, a dependency-breaking zero-latency instruction is expected to
- // be optimized at register renaming stage. That means, no physical register
- // is allocated to the instruction.
- bool ShouldAllocateRegisters =
- !(Desc.isZeroLatency() && IsDependencyBreaking);
- SmallVector<unsigned, 4> RegisterFiles(PRF.getNumRegisterFiles());
- for (std::unique_ptr<WriteState> &WS : IS.getDefs()) {
- PRF.addRegisterWrite(WriteRef(IR.first, WS.get()), RegisterFiles,
- ShouldAllocateRegisters);
- }
-
- // Reserve slots in the RCU, and notify the instruction that it has been
- // dispatched to the schedulers for execution.
- IS.dispatch(RCU.reserveSlot(IR, NumMicroOps));
-
- // Notify listeners of the "instruction dispatched" event.
- notifyInstructionDispatched(IR, RegisterFiles);
-}
-
-void DispatchStage::cycleStart() {
- AvailableEntries = CarryOver >= DispatchWidth ? 0 : DispatchWidth - CarryOver;
- CarryOver = CarryOver >= DispatchWidth ? CarryOver - DispatchWidth : 0U;
-}
-
-bool DispatchStage::execute(InstRef &IR) {
- const InstrDesc &Desc = IR.getInstruction()->getDesc();
- if (!isAvailable(Desc.NumMicroOps) || !canDispatch(IR))
- return false;
- dispatch(IR);
- return true;
-}
-
-#ifndef NDEBUG
-void DispatchStage::dump() const {
- PRF.dump();
- RCU.dump();
-}
-#endif
-} // namespace mca
diff --git a/tools/llvm-mca/DispatchStage.h b/tools/llvm-mca/DispatchStage.h
deleted file mode 100644
index 4262a241c08c..000000000000
--- a/tools/llvm-mca/DispatchStage.h
+++ /dev/null
@@ -1,106 +0,0 @@
-//===----------------------- DispatchStage.h --------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file models the dispatch component of an instruction pipeline.
-///
-/// The DispatchStage is responsible for updating instruction dependencies
-/// and communicating to the simulated instruction scheduler that an instruction
-/// is ready to be scheduled for execution.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_DISPATCH_STAGE_H
-#define LLVM_TOOLS_LLVM_MCA_DISPATCH_STAGE_H
-
-#include "HWEventListener.h"
-#include "Instruction.h"
-#include "RegisterFile.h"
-#include "RetireControlUnit.h"
-#include "Stage.h"
-#include "llvm/MC/MCRegisterInfo.h"
-#include "llvm/MC/MCSubtargetInfo.h"
-
-namespace mca {
-
-class Scheduler;
-
-// Implements the hardware dispatch logic.
-//
-// This class is responsible for the dispatch stage, in which instructions are
-// dispatched in groups to the Scheduler. An instruction can be dispatched if
-// the following conditions are met:
-// 1) There are enough entries in the reorder buffer (see class
-// RetireControlUnit) to write the opcodes associated with the instruction.
-// 2) There are enough physical registers to rename output register operands.
-// 3) There are enough entries available in the used buffered resource(s).
-//
-// The number of micro opcodes that can be dispatched in one cycle is limited by
-// the value of field 'DispatchWidth'. A "dynamic dispatch stall" occurs when
-// processor resources are not available. Dispatch stall events are counted
-// during the entire execution of the code, and displayed by the performance
-// report when flag '-dispatch-stats' is specified.
-//
-// If the number of micro opcodes exceedes DispatchWidth, then the instruction
-// is dispatched in multiple cycles.
-class DispatchStage : public Stage {
- unsigned DispatchWidth;
- unsigned AvailableEntries;
- unsigned CarryOver;
- const llvm::MCSubtargetInfo &STI;
- RetireControlUnit &RCU;
- RegisterFile &PRF;
- Scheduler &SC;
-
- bool checkRCU(const InstRef &IR);
- bool checkPRF(const InstRef &IR);
- bool checkScheduler(const InstRef &IR);
- void dispatch(InstRef IR);
- void updateRAWDependencies(ReadState &RS, const llvm::MCSubtargetInfo &STI);
-
- void notifyInstructionDispatched(const InstRef &IR,
- llvm::ArrayRef<unsigned> UsedPhysRegs);
-
- bool isAvailable(unsigned NumEntries) const {
- return NumEntries <= AvailableEntries || AvailableEntries == DispatchWidth;
- }
-
- bool canDispatch(const InstRef &IR) {
- assert(isAvailable(IR.getInstruction()->getDesc().NumMicroOps));
- return checkRCU(IR) && checkPRF(IR) && checkScheduler(IR);
- }
-
- void collectWrites(llvm::SmallVectorImpl<WriteRef> &Vec,
- unsigned RegID) const {
- return PRF.collectWrites(Vec, RegID);
- }
-
-public:
- DispatchStage(const llvm::MCSubtargetInfo &Subtarget,
- const llvm::MCRegisterInfo &MRI, unsigned RegisterFileSize,
- unsigned MaxDispatchWidth, RetireControlUnit &R,
- RegisterFile &F, Scheduler &Sched)
- : DispatchWidth(MaxDispatchWidth), AvailableEntries(MaxDispatchWidth),
- CarryOver(0U), STI(Subtarget), RCU(R), PRF(F), SC(Sched) {}
-
- // We can always try to dispatch, so returning false is okay in this case.
- // The retire stage, which controls the RCU, might have items to complete but
- // RetireStage::hasWorkToComplete will check for that case.
- virtual bool hasWorkToComplete() const override final { return false; }
- virtual void cycleStart() override final;
- virtual bool execute(InstRef &IR) override final;
- void notifyDispatchStall(const InstRef &IR, unsigned EventType);
-
-#ifndef NDEBUG
- void dump() const;
-#endif
-};
-} // namespace mca
-
-#endif // LLVM_TOOLS_LLVM_MCA_DISPATCH_STAGE_H
diff --git a/tools/llvm-mca/DispatchStatistics.cpp b/tools/llvm-mca/DispatchStatistics.cpp
deleted file mode 100644
index 4bddbef9a0c8..000000000000
--- a/tools/llvm-mca/DispatchStatistics.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-//===--------------------- DispatchStatistics.cpp ---------------------*- C++
-//-*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file implements the DispatchStatistics interface.
-///
-//===----------------------------------------------------------------------===//
-
-#include "DispatchStatistics.h"
-#include "llvm/Support/Format.h"
-
-using namespace llvm;
-
-namespace mca {
-
-void DispatchStatistics::onEvent(const HWStallEvent &Event) {
- if (Event.Type < HWStallEvent::LastGenericEvent)
- HWStalls[Event.Type]++;
-}
-
-void DispatchStatistics::onEvent(const HWInstructionEvent &Event) {
- if (Event.Type == HWInstructionEvent::Dispatched)
- ++NumDispatched;
-}
-
-void DispatchStatistics::printDispatchHistogram(llvm::raw_ostream &OS) const {
- std::string Buffer;
- raw_string_ostream TempStream(Buffer);
- TempStream << "\n\nDispatch Logic - "
- << "number of cycles where we saw N instructions dispatched:\n";
- TempStream << "[# dispatched], [# cycles]\n";
- for (const std::pair<unsigned, unsigned> &Entry : DispatchGroupSizePerCycle) {
- TempStream << " " << Entry.first << ", " << Entry.second
- << " ("
- << format("%.1f", ((double)Entry.second / NumCycles) * 100.0)
- << "%)\n";
- }
-
- TempStream.flush();
- OS << Buffer;
-}
-
-void DispatchStatistics::printDispatchStalls(raw_ostream &OS) const {
- std::string Buffer;
- raw_string_ostream TempStream(Buffer);
- TempStream << "\n\nDynamic Dispatch Stall Cycles:\n";
- TempStream << "RAT - Register unavailable: "
- << HWStalls[HWStallEvent::RegisterFileStall];
- TempStream << "\nRCU - Retire tokens unavailable: "
- << HWStalls[HWStallEvent::RetireControlUnitStall];
- TempStream << "\nSCHEDQ - Scheduler full: "
- << HWStalls[HWStallEvent::SchedulerQueueFull];
- TempStream << "\nLQ - Load queue full: "
- << HWStalls[HWStallEvent::LoadQueueFull];
- TempStream << "\nSQ - Store queue full: "
- << HWStalls[HWStallEvent::StoreQueueFull];
- TempStream << "\nGROUP - Static restrictions on the dispatch group: "
- << HWStalls[HWStallEvent::DispatchGroupStall];
- TempStream << '\n';
- TempStream.flush();
- OS << Buffer;
-}
-
-} // namespace mca
diff --git a/tools/llvm-mca/ExecuteStage.cpp b/tools/llvm-mca/ExecuteStage.cpp
deleted file mode 100644
index 437f864b072c..000000000000
--- a/tools/llvm-mca/ExecuteStage.cpp
+++ /dev/null
@@ -1,210 +0,0 @@
-//===---------------------- ExecuteStage.cpp --------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines the execution stage of an instruction pipeline.
-///
-/// The ExecuteStage is responsible for managing the hardware scheduler
-/// and issuing notifications that an instruction has been executed.
-///
-//===----------------------------------------------------------------------===//
-
-#include "ExecuteStage.h"
-#include "Scheduler.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/Support/Debug.h"
-
-#define DEBUG_TYPE "llvm-mca"
-
-namespace mca {
-
-using namespace llvm;
-
-// Reclaim the simulated resources used by the scheduler.
-void ExecuteStage::reclaimSchedulerResources() {
- SmallVector<ResourceRef, 8> ResourcesFreed;
- HWS.reclaimSimulatedResources(ResourcesFreed);
- for (const ResourceRef &RR : ResourcesFreed)
- notifyResourceAvailable(RR);
-}
-
-// Update the scheduler's instruction queues.
-void ExecuteStage::updateSchedulerQueues() {
- SmallVector<InstRef, 4> InstructionIDs;
- HWS.updateIssuedQueue(InstructionIDs);
- for (const InstRef &IR : InstructionIDs)
- notifyInstructionExecuted(IR);
- InstructionIDs.clear();
-
- HWS.updatePendingQueue(InstructionIDs);
- for (const InstRef &IR : InstructionIDs)
- notifyInstructionReady(IR);
-}
-
-// Issue instructions that are waiting in the scheduler's ready queue.
-void ExecuteStage::issueReadyInstructions() {
- SmallVector<InstRef, 4> InstructionIDs;
- InstRef IR = HWS.select();
- while (IR.isValid()) {
- SmallVector<std::pair<ResourceRef, double>, 4> Used;
- HWS.issueInstruction(IR, Used);
-
- // Reclaim instruction resources and perform notifications.
- const InstrDesc &Desc = IR.getInstruction()->getDesc();
- notifyReleasedBuffers(Desc.Buffers);
- notifyInstructionIssued(IR, Used);
- if (IR.getInstruction()->isExecuted())
- notifyInstructionExecuted(IR);
-
- // Instructions that have been issued during this cycle might have unblocked
- // other dependent instructions. Dependent instructions may be issued during
- // this same cycle if operands have ReadAdvance entries. Promote those
- // instructions to the ReadyQueue and tell to the caller that we need
- // another round of 'issue()'.
- HWS.promoteToReadyQueue(InstructionIDs);
- for (const InstRef &I : InstructionIDs)
- notifyInstructionReady(I);
- InstructionIDs.clear();
-
- // Select the next instruction to issue.
- IR = HWS.select();
- }
-}
-
-// The following routine is the maintenance routine of the ExecuteStage.
-// It is responsible for updating the hardware scheduler (HWS), including
-// reclaiming the HWS's simulated hardware resources, as well as updating the
-// HWS's queues.
-//
-// This routine also processes the instructions that are ready for issuance.
-// These instructions are managed by the HWS's ready queue and can be accessed
-// via the Scheduler::select() routine.
-//
-// Notifications are issued to this stage's listeners when instructions are
-// moved between the HWS's queues. In particular, when an instruction becomes
-// ready or executed.
-void ExecuteStage::cycleStart() {
- reclaimSchedulerResources();
- updateSchedulerQueues();
- issueReadyInstructions();
-}
-
-// Schedule the instruction for execution on the hardware.
-bool ExecuteStage::execute(InstRef &IR) {
-#ifndef NDEBUG
- // Ensure that the HWS has not stored this instruction in its queues.
- HWS.sanityCheck(IR);
-#endif
- // Reserve a slot in each buffered resource. Also, mark units with
- // BufferSize=0 as reserved. Resources with a buffer size of zero will only
- // be released after MCIS is issued, and all the ResourceCycles for those
- // units have been consumed.
- const InstrDesc &Desc = IR.getInstruction()->getDesc();
- HWS.reserveBuffers(Desc.Buffers);
- notifyReservedBuffers(Desc.Buffers);
-
- // Obtain a slot in the LSU. If we cannot reserve resources, return true, so
- // that succeeding stages can make progress.
- if (!HWS.reserveResources(IR))
- return true;
-
- // If we did not return early, then the scheduler is ready for execution.
- notifyInstructionReady(IR);
-
- // Don't add a zero-latency instruction to the Wait or Ready queue.
- // A zero-latency instruction doesn't consume any scheduler resources. That is
- // because it doesn't need to be executed, and it is often removed at register
- // renaming stage. For example, register-register moves are often optimized at
- // register renaming stage by simply updating register aliases. On some
- // targets, zero-idiom instructions (for example: a xor that clears the value
- // of a register) are treated specially, and are often eliminated at register
- // renaming stage.
- //
- // Instructions that use an in-order dispatch/issue processor resource must be
- // issued immediately to the pipeline(s). Any other in-order buffered
- // resources (i.e. BufferSize=1) is consumed.
- //
- // If we cannot issue immediately, the HWS will add IR to its ready queue for
- // execution later, so we must return early here.
- if (!HWS.issueImmediately(IR))
- return true;
-
- LLVM_DEBUG(dbgs() << "[SCHEDULER] Instruction #" << IR
- << " issued immediately\n");
-
- // Issue IR. The resources for this issuance will be placed in 'Used.'
- SmallVector<std::pair<ResourceRef, double>, 4> Used;
- HWS.issueInstruction(IR, Used);
-
- // Perform notifications.
- notifyReleasedBuffers(Desc.Buffers);
- notifyInstructionIssued(IR, Used);
- if (IR.getInstruction()->isExecuted())
- notifyInstructionExecuted(IR);
-
- return true;
-}
-
-void ExecuteStage::notifyInstructionExecuted(const InstRef &IR) {
- HWS.onInstructionExecuted(IR);
- LLVM_DEBUG(dbgs() << "[E] Instruction Executed: #" << IR << '\n');
- notifyEvent<HWInstructionEvent>(
- HWInstructionEvent(HWInstructionEvent::Executed, IR));
- RCU.onInstructionExecuted(IR.getInstruction()->getRCUTokenID());
-}
-
-void ExecuteStage::notifyInstructionReady(const InstRef &IR) {
- LLVM_DEBUG(dbgs() << "[E] Instruction Ready: #" << IR << '\n');
- notifyEvent<HWInstructionEvent>(
- HWInstructionEvent(HWInstructionEvent::Ready, IR));
-}
-
-void ExecuteStage::notifyResourceAvailable(const ResourceRef &RR) {
- LLVM_DEBUG(dbgs() << "[E] Resource Available: [" << RR.first << '.'
- << RR.second << "]\n");
- for (HWEventListener *Listener : getListeners())
- Listener->onResourceAvailable(RR);
-}
-
-void ExecuteStage::notifyInstructionIssued(
- const InstRef &IR, ArrayRef<std::pair<ResourceRef, double>> Used) {
- LLVM_DEBUG({
- dbgs() << "[E] Instruction Issued: #" << IR << '\n';
- for (const std::pair<ResourceRef, unsigned> &Resource : Used) {
- dbgs() << "[E] Resource Used: [" << Resource.first.first << '.'
- << Resource.first.second << "], ";
- dbgs() << "cycles: " << Resource.second << '\n';
- }
- });
- notifyEvent<HWInstructionEvent>(HWInstructionIssuedEvent(IR, Used));
-}
-
-void ExecuteStage::notifyReservedBuffers(ArrayRef<uint64_t> Buffers) {
- if (Buffers.empty())
- return;
-
- SmallVector<unsigned, 4> BufferIDs(Buffers.begin(), Buffers.end());
- std::transform(Buffers.begin(), Buffers.end(), BufferIDs.begin(),
- [&](uint64_t Op) { return HWS.getResourceID(Op); });
- for (HWEventListener *Listener : getListeners())
- Listener->onReservedBuffers(BufferIDs);
-}
-
-void ExecuteStage::notifyReleasedBuffers(ArrayRef<uint64_t> Buffers) {
- if (Buffers.empty())
- return;
-
- SmallVector<unsigned, 4> BufferIDs(Buffers.begin(), Buffers.end());
- std::transform(Buffers.begin(), Buffers.end(), BufferIDs.begin(),
- [&](uint64_t Op) { return HWS.getResourceID(Op); });
- for (HWEventListener *Listener : getListeners())
- Listener->onReleasedBuffers(BufferIDs);
-}
-
-} // namespace mca
diff --git a/tools/llvm-mca/ExecuteStage.h b/tools/llvm-mca/ExecuteStage.h
deleted file mode 100644
index 4914a9373e7c..000000000000
--- a/tools/llvm-mca/ExecuteStage.h
+++ /dev/null
@@ -1,67 +0,0 @@
-//===---------------------- ExecuteStage.h ----------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines the execution stage of an instruction pipeline.
-///
-/// The ExecuteStage is responsible for managing the hardware scheduler
-/// and issuing notifications that an instruction has been executed.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_EXECUTE_STAGE_H
-#define LLVM_TOOLS_LLVM_MCA_EXECUTE_STAGE_H
-
-#include "Instruction.h"
-#include "RetireControlUnit.h"
-#include "Scheduler.h"
-#include "Stage.h"
-#include "llvm/ADT/ArrayRef.h"
-
-namespace mca {
-
-class ExecuteStage : public Stage {
- // Owner will go away when we move listeners/eventing to the stages.
- RetireControlUnit &RCU;
- Scheduler &HWS;
-
- // The following routines are used to maintain the HWS.
- void reclaimSchedulerResources();
- void updateSchedulerQueues();
- void issueReadyInstructions();
-
-public:
- ExecuteStage(RetireControlUnit &R, Scheduler &S) : Stage(), RCU(R), HWS(S) {}
- ExecuteStage(const ExecuteStage &Other) = delete;
- ExecuteStage &operator=(const ExecuteStage &Other) = delete;
-
- // The ExecuteStage will always complete all of its work per call to
- // execute(), so it is never left in a 'to-be-processed' state.
- virtual bool hasWorkToComplete() const override final { return false; }
-
- virtual void cycleStart() override final;
- virtual bool execute(InstRef &IR) override final;
-
- void
- notifyInstructionIssued(const InstRef &IR,
- llvm::ArrayRef<std::pair<ResourceRef, double>> Used);
- void notifyInstructionExecuted(const InstRef &IR);
- void notifyInstructionReady(const InstRef &IR);
- void notifyResourceAvailable(const ResourceRef &RR);
-
- // Notify listeners that buffered resources were consumed.
- void notifyReservedBuffers(llvm::ArrayRef<uint64_t> Buffers);
-
- // Notify listeners that buffered resources were freed.
- void notifyReleasedBuffers(llvm::ArrayRef<uint64_t> Buffers);
-};
-
-} // namespace mca
-
-#endif // LLVM_TOOLS_LLVM_MCA_EXECUTE_STAGE_H
diff --git a/tools/llvm-mca/FetchStage.cpp b/tools/llvm-mca/FetchStage.cpp
deleted file mode 100644
index 3da117c0abc1..000000000000
--- a/tools/llvm-mca/FetchStage.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-//===---------------------- FetchStage.cpp ----------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines the Fetch stage of an instruction pipeline. Its sole
-/// purpose in life is to produce instructions for the rest of the pipeline.
-///
-//===----------------------------------------------------------------------===//
-
-#include "FetchStage.h"
-
-namespace mca {
-
-bool FetchStage::hasWorkToComplete() const { return SM.hasNext(); }
-
-bool FetchStage::execute(InstRef &IR) {
- if (!SM.hasNext())
- return false;
- const SourceRef SR = SM.peekNext();
- std::unique_ptr<Instruction> I = IB.createInstruction(*SR.second);
- IR = InstRef(SR.first, I.get());
- Instructions[IR.getSourceIndex()] = std::move(I);
- return true;
-}
-
-void FetchStage::postExecute() { SM.updateNext(); }
-
-void FetchStage::cycleEnd() {
- // Find the first instruction which hasn't been retired.
- const InstMap::iterator It =
- llvm::find_if(Instructions, [](const InstMap::value_type &KeyValuePair) {
- return !KeyValuePair.second->isRetired();
- });
-
- // Erase instructions up to the first that hasn't been retired.
- if (It != Instructions.begin())
- Instructions.erase(Instructions.begin(), It);
-}
-
-} // namespace mca
diff --git a/tools/llvm-mca/FetchStage.h b/tools/llvm-mca/FetchStage.h
deleted file mode 100644
index 620075d24fea..000000000000
--- a/tools/llvm-mca/FetchStage.h
+++ /dev/null
@@ -1,45 +0,0 @@
-//===---------------------- FetchStage.h ------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines the Fetch stage of an instruction pipeline. Its sole
-/// purpose in life is to produce instructions for the rest of the pipeline.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_FETCH_STAGE_H
-#define LLVM_TOOLS_LLVM_MCA_FETCH_STAGE_H
-
-#include "InstrBuilder.h"
-#include "SourceMgr.h"
-#include "Stage.h"
-#include <map>
-
-namespace mca {
-
-class FetchStage : public Stage {
- using InstMap = std::map<unsigned, std::unique_ptr<Instruction>>;
- InstMap Instructions;
- InstrBuilder &IB;
- SourceMgr &SM;
-
-public:
- FetchStage(InstrBuilder &IB, SourceMgr &SM) : IB(IB), SM(SM) {}
- FetchStage(const FetchStage &Other) = delete;
- FetchStage &operator=(const FetchStage &Other) = delete;
-
- bool hasWorkToComplete() const override final;
- bool execute(InstRef &IR) override final;
- void postExecute() override final;
- void cycleEnd() override final;
-};
-
-} // namespace mca
-
-#endif // LLVM_TOOLS_LLVM_MCA_FETCH_STAGE_H
diff --git a/tools/llvm-mca/HWEventListener.h b/tools/llvm-mca/HWEventListener.h
deleted file mode 100644
index aa3e6dcf19a0..000000000000
--- a/tools/llvm-mca/HWEventListener.h
+++ /dev/null
@@ -1,141 +0,0 @@
-//===----------------------- HWEventListener.h ------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines the main interface for hardware event listeners.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_HWEVENTLISTENER_H
-#define LLVM_TOOLS_LLVM_MCA_HWEVENTLISTENER_H
-
-#include "Instruction.h"
-#include "llvm/ADT/ArrayRef.h"
-#include <utility>
-
-namespace mca {
-
-// An HWInstructionEvent represents state changes of instructions that
-// listeners might be interested in. Listeners can choose to ignore any event
-// they are not interested in.
-class HWInstructionEvent {
-public:
- // This is the list of event types that are shared by all targets, that
- // generic subtarget-agnostic classes (e.g., Pipeline, HWInstructionEvent,
- // ...) and generic Views can manipulate.
- // Subtargets are free to define additional event types, that are goin to be
- // handled by generic components as opaque values, but can still be
- // emitted by subtarget-specific pipeline stages (e.g., ExecuteStage,
- // DispatchStage, ...) and interpreted by subtarget-specific EventListener
- // implementations.
- enum GenericEventType {
- Invalid = 0,
- // Events generated by the Retire Control Unit.
- Retired,
- // Events generated by the Scheduler.
- Ready,
- Issued,
- Executed,
- // Events generated by the Dispatch logic.
- Dispatched,
-
- LastGenericEventType,
- };
-
- HWInstructionEvent(unsigned type, const InstRef &Inst)
- : Type(type), IR(Inst) {}
-
- // The event type. The exact meaning depends on the subtarget.
- const unsigned Type;
-
- // The instruction this event was generated for.
- const InstRef &IR;
-};
-
-class HWInstructionIssuedEvent : public HWInstructionEvent {
-public:
- using ResourceRef = std::pair<uint64_t, uint64_t>;
- HWInstructionIssuedEvent(const InstRef &IR,
- llvm::ArrayRef<std::pair<ResourceRef, double>> UR)
- : HWInstructionEvent(HWInstructionEvent::Issued, IR), UsedResources(UR) {}
-
- llvm::ArrayRef<std::pair<ResourceRef, double>> UsedResources;
-};
-
-class HWInstructionDispatchedEvent : public HWInstructionEvent {
-public:
- HWInstructionDispatchedEvent(const InstRef &IR, llvm::ArrayRef<unsigned> Regs)
- : HWInstructionEvent(HWInstructionEvent::Dispatched, IR),
- UsedPhysRegs(Regs) {}
- // Number of physical register allocated for this instruction. There is one
- // entry per register file.
- llvm::ArrayRef<unsigned> UsedPhysRegs;
-};
-
-class HWInstructionRetiredEvent : public HWInstructionEvent {
-public:
- HWInstructionRetiredEvent(const InstRef &IR, llvm::ArrayRef<unsigned> Regs)
- : HWInstructionEvent(HWInstructionEvent::Retired, IR),
- FreedPhysRegs(Regs) {}
- // Number of register writes that have been architecturally committed. There
- // is one entry per register file.
- llvm::ArrayRef<unsigned> FreedPhysRegs;
-};
-
-// A HWStallEvent represents a pipeline stall caused by the lack of hardware
-// resources.
-class HWStallEvent {
-public:
- enum GenericEventType {
- Invalid = 0,
- // Generic stall events generated by the DispatchStage.
- RegisterFileStall,
- RetireControlUnitStall,
- // Generic stall events generated by the Scheduler.
- DispatchGroupStall,
- SchedulerQueueFull,
- LoadQueueFull,
- StoreQueueFull,
- LastGenericEvent
- };
-
- HWStallEvent(unsigned type, const InstRef &Inst) : Type(type), IR(Inst) {}
-
- // The exact meaning of the stall event type depends on the subtarget.
- const unsigned Type;
-
- // The instruction this event was generated for.
- const InstRef &IR;
-};
-
-class HWEventListener {
-public:
- // Generic events generated by the pipeline.
- virtual void onCycleBegin() {}
- virtual void onCycleEnd() {}
-
- virtual void onEvent(const HWInstructionEvent &Event) {}
- virtual void onEvent(const HWStallEvent &Event) {}
-
- using ResourceRef = std::pair<uint64_t, uint64_t>;
- virtual void onResourceAvailable(const ResourceRef &RRef) {}
-
- // Events generated by the Scheduler when buffered resources are
- // consumed/freed.
- virtual void onReservedBuffers(llvm::ArrayRef<unsigned> Buffers) {}
- virtual void onReleasedBuffers(llvm::ArrayRef<unsigned> Buffers) {}
-
- virtual ~HWEventListener() {}
-
-private:
- virtual void anchor();
-};
-} // namespace mca
-
-#endif
diff --git a/tools/llvm-mca/HardwareUnit.cpp b/tools/llvm-mca/HardwareUnit.cpp
deleted file mode 100644
index 103cde9afcc8..000000000000
--- a/tools/llvm-mca/HardwareUnit.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-//===------------------------- HardwareUnit.cpp -----------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines the anchor for the base class that describes
-/// simulated hardware units.
-///
-//===----------------------------------------------------------------------===//
-
-#include "HardwareUnit.h"
-
-namespace mca {
-
-// Pin the vtable with this method.
-HardwareUnit::~HardwareUnit() = default;
-
-} // namespace mca
diff --git a/tools/llvm-mca/HardwareUnit.h b/tools/llvm-mca/HardwareUnit.h
deleted file mode 100644
index e8c496ab967a..000000000000
--- a/tools/llvm-mca/HardwareUnit.h
+++ /dev/null
@@ -1,31 +0,0 @@
-//===-------------------------- HardwareUnit.h ------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines a base class for describing a simulated hardware
-/// unit. These units are used to construct a simulated backend.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_HARDWAREUNIT_H
-#define LLVM_TOOLS_LLVM_MCA_HARDWAREUNIT_H
-
-namespace mca {
-
-class HardwareUnit {
- HardwareUnit(const HardwareUnit &H) = delete;
- HardwareUnit &operator=(const HardwareUnit &H) = delete;
-
-public:
- HardwareUnit() = default;
- virtual ~HardwareUnit();
-};
-
-} // namespace mca
-#endif // LLVM_TOOLS_LLVM_MCA_HARDWAREUNIT_H
diff --git a/tools/llvm-mca/InstrBuilder.cpp b/tools/llvm-mca/InstrBuilder.cpp
deleted file mode 100644
index 053b7b4e8175..000000000000
--- a/tools/llvm-mca/InstrBuilder.cpp
+++ /dev/null
@@ -1,469 +0,0 @@
-//===--------------------- InstrBuilder.cpp ---------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file implements the InstrBuilder interface.
-///
-//===----------------------------------------------------------------------===//
-
-#include "InstrBuilder.h"
-#include "llvm/ADT/APInt.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/MC/MCInst.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/WithColor.h"
-#include "llvm/Support/raw_ostream.h"
-
-#define DEBUG_TYPE "llvm-mca"
-
-namespace mca {
-
-using namespace llvm;
-
-static void initializeUsedResources(InstrDesc &ID,
- const MCSchedClassDesc &SCDesc,
- const MCSubtargetInfo &STI,
- ArrayRef<uint64_t> ProcResourceMasks) {
- const MCSchedModel &SM = STI.getSchedModel();
-
- // Populate resources consumed.
- using ResourcePlusCycles = std::pair<uint64_t, ResourceUsage>;
- std::vector<ResourcePlusCycles> Worklist;
-
- // Track cycles contributed by resources that are in a "Super" relationship.
- // This is required if we want to correctly match the behavior of method
- // SubtargetEmitter::ExpandProcResource() in Tablegen. When computing the set
- // of "consumed" processor resources and resource cycles, the logic in
- // ExpandProcResource() doesn't update the number of resource cycles
- // contributed by a "Super" resource to a group.
- // We need to take this into account when we find that a processor resource is
- // part of a group, and it is also used as the "Super" of other resources.
- // This map stores the number of cycles contributed by sub-resources that are
- // part of a "Super" resource. The key value is the "Super" resource mask ID.
- DenseMap<uint64_t, unsigned> SuperResources;
-
- for (unsigned I = 0, E = SCDesc.NumWriteProcResEntries; I < E; ++I) {
- const MCWriteProcResEntry *PRE = STI.getWriteProcResBegin(&SCDesc) + I;
- const MCProcResourceDesc &PR = *SM.getProcResource(PRE->ProcResourceIdx);
- uint64_t Mask = ProcResourceMasks[PRE->ProcResourceIdx];
- if (PR.BufferSize != -1)
- ID.Buffers.push_back(Mask);
- CycleSegment RCy(0, PRE->Cycles, false);
- Worklist.emplace_back(ResourcePlusCycles(Mask, ResourceUsage(RCy)));
- if (PR.SuperIdx) {
- uint64_t Super = ProcResourceMasks[PR.SuperIdx];
- SuperResources[Super] += PRE->Cycles;
- }
- }
-
- // Sort elements by mask popcount, so that we prioritize resource units over
- // resource groups, and smaller groups over larger groups.
- llvm::sort(Worklist.begin(), Worklist.end(),
- [](const ResourcePlusCycles &A, const ResourcePlusCycles &B) {
- unsigned popcntA = countPopulation(A.first);
- unsigned popcntB = countPopulation(B.first);
- if (popcntA < popcntB)
- return true;
- if (popcntA > popcntB)
- return false;
- return A.first < B.first;
- });
-
- uint64_t UsedResourceUnits = 0;
-
- // Remove cycles contributed by smaller resources.
- for (unsigned I = 0, E = Worklist.size(); I < E; ++I) {
- ResourcePlusCycles &A = Worklist[I];
- if (!A.second.size()) {
- A.second.NumUnits = 0;
- A.second.setReserved();
- ID.Resources.emplace_back(A);
- continue;
- }
-
- ID.Resources.emplace_back(A);
- uint64_t NormalizedMask = A.first;
- if (countPopulation(A.first) == 1) {
- UsedResourceUnits |= A.first;
- } else {
- // Remove the leading 1 from the resource group mask.
- NormalizedMask ^= PowerOf2Floor(NormalizedMask);
- }
-
- for (unsigned J = I + 1; J < E; ++J) {
- ResourcePlusCycles &B = Worklist[J];
- if ((NormalizedMask & B.first) == NormalizedMask) {
- B.second.CS.Subtract(A.second.size() - SuperResources[A.first]);
- if (countPopulation(B.first) > 1)
- B.second.NumUnits++;
- }
- }
- }
-
- // A SchedWrite may specify a number of cycles in which a resource group
- // is reserved. For example (on target x86; cpu Haswell):
- //
- // SchedWriteRes<[HWPort0, HWPort1, HWPort01]> {
- // let ResourceCycles = [2, 2, 3];
- // }
- //
- // This means:
- // Resource units HWPort0 and HWPort1 are both used for 2cy.
- // Resource group HWPort01 is the union of HWPort0 and HWPort1.
- // Since this write touches both HWPort0 and HWPort1 for 2cy, HWPort01
- // will not be usable for 2 entire cycles from instruction issue.
- //
- // On top of those 2cy, SchedWriteRes explicitly specifies an extra latency
- // of 3 cycles for HWPort01. This tool assumes that the 3cy latency is an
- // extra delay on top of the 2 cycles latency.
- // During those extra cycles, HWPort01 is not usable by other instructions.
- for (ResourcePlusCycles &RPC : ID.Resources) {
- if (countPopulation(RPC.first) > 1 && !RPC.second.isReserved()) {
- // Remove the leading 1 from the resource group mask.
- uint64_t Mask = RPC.first ^ PowerOf2Floor(RPC.first);
- if ((Mask & UsedResourceUnits) == Mask)
- RPC.second.setReserved();
- }
- }
-
- LLVM_DEBUG({
- for (const std::pair<uint64_t, ResourceUsage> &R : ID.Resources)
- dbgs() << "\t\tMask=" << R.first << ", cy=" << R.second.size() << '\n';
- for (const uint64_t R : ID.Buffers)
- dbgs() << "\t\tBuffer Mask=" << R << '\n';
- });
-}
-
-static void computeMaxLatency(InstrDesc &ID, const MCInstrDesc &MCDesc,
- const MCSchedClassDesc &SCDesc,
- const MCSubtargetInfo &STI) {
- if (MCDesc.isCall()) {
- // We cannot estimate how long this call will take.
- // Artificially set an arbitrarily high latency (100cy).
- ID.MaxLatency = 100U;
- return;
- }
-
- int Latency = MCSchedModel::computeInstrLatency(STI, SCDesc);
- // If latency is unknown, then conservatively assume a MaxLatency of 100cy.
- ID.MaxLatency = Latency < 0 ? 100U : static_cast<unsigned>(Latency);
-}
-
-void InstrBuilder::populateWrites(InstrDesc &ID, const MCInst &MCI,
- unsigned SchedClassID) {
- const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
- const MCSchedModel &SM = STI.getSchedModel();
- const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
-
- // These are for now the (strong) assumptions made by this algorithm:
- // * The number of explicit and implicit register definitions in a MCInst
- // matches the number of explicit and implicit definitions according to
- // the opcode descriptor (MCInstrDesc).
- // * Register definitions take precedence over register uses in the operands
- // list.
- // * If an opcode specifies an optional definition, then the optional
- // definition is always the last operand in the sequence, and it can be
- // set to zero (i.e. "no register").
- //
- // These assumptions work quite well for most out-of-order in-tree targets
- // like x86. This is mainly because the vast majority of instructions is
- // expanded to MCInst using a straightforward lowering logic that preserves
- // the ordering of the operands.
- unsigned NumExplicitDefs = MCDesc.getNumDefs();
- unsigned NumImplicitDefs = MCDesc.getNumImplicitDefs();
- unsigned NumWriteLatencyEntries = SCDesc.NumWriteLatencyEntries;
- unsigned TotalDefs = NumExplicitDefs + NumImplicitDefs;
- if (MCDesc.hasOptionalDef())
- TotalDefs++;
- ID.Writes.resize(TotalDefs);
- // Iterate over the operands list, and skip non-register operands.
- // The first NumExplictDefs register operands are expected to be register
- // definitions.
- unsigned CurrentDef = 0;
- unsigned i = 0;
- for (; i < MCI.getNumOperands() && CurrentDef < NumExplicitDefs; ++i) {
- const MCOperand &Op = MCI.getOperand(i);
- if (!Op.isReg())
- continue;
-
- WriteDescriptor &Write = ID.Writes[CurrentDef];
- Write.OpIndex = i;
- if (CurrentDef < NumWriteLatencyEntries) {
- const MCWriteLatencyEntry &WLE =
- *STI.getWriteLatencyEntry(&SCDesc, CurrentDef);
- // Conservatively default to MaxLatency.
- Write.Latency =
- WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles);
- Write.SClassOrWriteResourceID = WLE.WriteResourceID;
- } else {
- // Assign a default latency for this write.
- Write.Latency = ID.MaxLatency;
- Write.SClassOrWriteResourceID = 0;
- }
- Write.IsOptionalDef = false;
- LLVM_DEBUG({
- dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex
- << ", Latency=" << Write.Latency
- << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
- });
- CurrentDef++;
- }
-
- if (CurrentDef != NumExplicitDefs)
- llvm::report_fatal_error(
- "error: Expected more register operand definitions. ");
-
- CurrentDef = 0;
- for (CurrentDef = 0; CurrentDef < NumImplicitDefs; ++CurrentDef) {
- unsigned Index = NumExplicitDefs + CurrentDef;
- WriteDescriptor &Write = ID.Writes[Index];
- Write.OpIndex = ~CurrentDef;
- Write.RegisterID = MCDesc.getImplicitDefs()[CurrentDef];
- if (Index < NumWriteLatencyEntries) {
- const MCWriteLatencyEntry &WLE =
- *STI.getWriteLatencyEntry(&SCDesc, Index);
- // Conservatively default to MaxLatency.
- Write.Latency =
- WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles);
- Write.SClassOrWriteResourceID = WLE.WriteResourceID;
- } else {
- // Assign a default latency for this write.
- Write.Latency = ID.MaxLatency;
- Write.SClassOrWriteResourceID = 0;
- }
-
- Write.IsOptionalDef = false;
- assert(Write.RegisterID != 0 && "Expected a valid phys register!");
- LLVM_DEBUG({
- dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex
- << ", PhysReg=" << MRI.getName(Write.RegisterID)
- << ", Latency=" << Write.Latency
- << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
- });
- }
-
- if (MCDesc.hasOptionalDef()) {
- // Always assume that the optional definition is the last operand of the
- // MCInst sequence.
- const MCOperand &Op = MCI.getOperand(MCI.getNumOperands() - 1);
- if (i == MCI.getNumOperands() || !Op.isReg())
- llvm::report_fatal_error(
- "error: expected a register operand for an optional "
- "definition. Instruction has not be correctly analyzed.\n",
- false);
-
- WriteDescriptor &Write = ID.Writes[TotalDefs - 1];
- Write.OpIndex = MCI.getNumOperands() - 1;
- // Assign a default latency for this write.
- Write.Latency = ID.MaxLatency;
- Write.SClassOrWriteResourceID = 0;
- Write.IsOptionalDef = true;
- }
-}
-
-void InstrBuilder::populateReads(InstrDesc &ID, const MCInst &MCI,
- unsigned SchedClassID) {
- const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
- unsigned NumExplicitDefs = MCDesc.getNumDefs();
-
- // Skip explicit definitions.
- unsigned i = 0;
- for (; i < MCI.getNumOperands() && NumExplicitDefs; ++i) {
- const MCOperand &Op = MCI.getOperand(i);
- if (Op.isReg())
- NumExplicitDefs--;
- }
-
- if (NumExplicitDefs)
- llvm::report_fatal_error(
- "error: Expected more register operand definitions. ", false);
-
- unsigned NumExplicitUses = MCI.getNumOperands() - i;
- unsigned NumImplicitUses = MCDesc.getNumImplicitUses();
- if (MCDesc.hasOptionalDef()) {
- assert(NumExplicitUses);
- NumExplicitUses--;
- }
- unsigned TotalUses = NumExplicitUses + NumImplicitUses;
- if (!TotalUses)
- return;
-
- ID.Reads.resize(TotalUses);
- for (unsigned CurrentUse = 0; CurrentUse < NumExplicitUses; ++CurrentUse) {
- ReadDescriptor &Read = ID.Reads[CurrentUse];
- Read.OpIndex = i + CurrentUse;
- Read.UseIndex = CurrentUse;
- Read.SchedClassID = SchedClassID;
- LLVM_DEBUG(dbgs() << "\t\t[Use] OpIdx=" << Read.OpIndex
- << ", UseIndex=" << Read.UseIndex << '\n');
- }
-
- for (unsigned CurrentUse = 0; CurrentUse < NumImplicitUses; ++CurrentUse) {
- ReadDescriptor &Read = ID.Reads[NumExplicitUses + CurrentUse];
- Read.OpIndex = ~CurrentUse;
- Read.UseIndex = NumExplicitUses + CurrentUse;
- Read.RegisterID = MCDesc.getImplicitUses()[CurrentUse];
- Read.SchedClassID = SchedClassID;
- LLVM_DEBUG(dbgs() << "\t\t[Use] OpIdx=" << Read.OpIndex << ", RegisterID="
- << MRI.getName(Read.RegisterID) << '\n');
- }
-}
-
-const InstrDesc &InstrBuilder::createInstrDescImpl(const MCInst &MCI) {
- assert(STI.getSchedModel().hasInstrSchedModel() &&
- "Itineraries are not yet supported!");
-
- // Obtain the instruction descriptor from the opcode.
- unsigned short Opcode = MCI.getOpcode();
- const MCInstrDesc &MCDesc = MCII.get(Opcode);
- const MCSchedModel &SM = STI.getSchedModel();
-
- // Then obtain the scheduling class information from the instruction.
- unsigned SchedClassID = MCDesc.getSchedClass();
- unsigned CPUID = SM.getProcessorID();
-
- // Try to solve variant scheduling classes.
- if (SchedClassID) {
- while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant())
- SchedClassID = STI.resolveVariantSchedClass(SchedClassID, &MCI, CPUID);
-
- if (!SchedClassID)
- llvm::report_fatal_error("unable to resolve this variant class.");
- }
-
- // Check if this instruction is supported. Otherwise, report a fatal error.
- const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
- if (SCDesc.NumMicroOps == MCSchedClassDesc::InvalidNumMicroOps) {
- std::string ToString;
- llvm::raw_string_ostream OS(ToString);
- WithColor::error() << "found an unsupported instruction in the input"
- << " assembly sequence.\n";
- MCIP.printInst(&MCI, OS, "", STI);
- OS.flush();
-
- WithColor::note() << "instruction: " << ToString << '\n';
- llvm::report_fatal_error(
- "Don't know how to analyze unsupported instructions.");
- }
-
- // Create a new empty descriptor.
- std::unique_ptr<InstrDesc> ID = llvm::make_unique<InstrDesc>();
- ID->NumMicroOps = SCDesc.NumMicroOps;
-
- if (MCDesc.isCall()) {
- // We don't correctly model calls.
- WithColor::warning() << "found a call in the input assembly sequence.\n";
- WithColor::note() << "call instructions are not correctly modeled. "
- << "Assume a latency of 100cy.\n";
- }
-
- if (MCDesc.isReturn()) {
- WithColor::warning() << "found a return instruction in the input"
- << " assembly sequence.\n";
- WithColor::note() << "program counter updates are ignored.\n";
- }
-
- ID->MayLoad = MCDesc.mayLoad();
- ID->MayStore = MCDesc.mayStore();
- ID->HasSideEffects = MCDesc.hasUnmodeledSideEffects();
-
- initializeUsedResources(*ID, SCDesc, STI, ProcResourceMasks);
- computeMaxLatency(*ID, MCDesc, SCDesc, STI);
- populateWrites(*ID, MCI, SchedClassID);
- populateReads(*ID, MCI, SchedClassID);
-
- LLVM_DEBUG(dbgs() << "\t\tMaxLatency=" << ID->MaxLatency << '\n');
- LLVM_DEBUG(dbgs() << "\t\tNumMicroOps=" << ID->NumMicroOps << '\n');
-
- // Now add the new descriptor.
- SchedClassID = MCDesc.getSchedClass();
- if (!SM.getSchedClassDesc(SchedClassID)->isVariant()) {
- Descriptors[MCI.getOpcode()] = std::move(ID);
- return *Descriptors[MCI.getOpcode()];
- }
-
- VariantDescriptors[&MCI] = std::move(ID);
- return *VariantDescriptors[&MCI];
-}
-
-const InstrDesc &InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) {
- if (Descriptors.find_as(MCI.getOpcode()) != Descriptors.end())
- return *Descriptors[MCI.getOpcode()];
-
- if (VariantDescriptors.find(&MCI) != VariantDescriptors.end())
- return *VariantDescriptors[&MCI];
-
- return createInstrDescImpl(MCI);
-}
-
-std::unique_ptr<Instruction>
-InstrBuilder::createInstruction(const MCInst &MCI) {
- const InstrDesc &D = getOrCreateInstrDesc(MCI);
- std::unique_ptr<Instruction> NewIS = llvm::make_unique<Instruction>(D);
-
- // Initialize Reads first.
- for (const ReadDescriptor &RD : D.Reads) {
- int RegID = -1;
- if (!RD.isImplicitRead()) {
- // explicit read.
- const MCOperand &Op = MCI.getOperand(RD.OpIndex);
- // Skip non-register operands.
- if (!Op.isReg())
- continue;
- RegID = Op.getReg();
- } else {
- // Implicit read.
- RegID = RD.RegisterID;
- }
-
- // Skip invalid register operands.
- if (!RegID)
- continue;
-
- // Okay, this is a register operand. Create a ReadState for it.
- assert(RegID > 0 && "Invalid register ID found!");
- NewIS->getUses().emplace_back(llvm::make_unique<ReadState>(RD, RegID));
- }
-
- // Early exit if there are no writes.
- if (D.Writes.empty())
- return NewIS;
-
- // Track register writes that implicitly clear the upper portion of the
- // underlying super-registers using an APInt.
- APInt WriteMask(D.Writes.size(), 0);
-
- // Now query the MCInstrAnalysis object to obtain information about which
- // register writes implicitly clear the upper portion of a super-register.
- MCIA.clearsSuperRegisters(MRI, MCI, WriteMask);
-
- // Check if this is a dependency breaking instruction.
- if (MCIA.isDependencyBreaking(STI, MCI))
- NewIS->setDependencyBreaking();
-
- // Initialize writes.
- unsigned WriteIndex = 0;
- for (const WriteDescriptor &WD : D.Writes) {
- unsigned RegID = WD.isImplicitWrite() ? WD.RegisterID
- : MCI.getOperand(WD.OpIndex).getReg();
- // Check if this is a optional definition that references NoReg.
- if (WD.IsOptionalDef && !RegID) {
- ++WriteIndex;
- continue;
- }
-
- assert(RegID && "Expected a valid register ID!");
- NewIS->getDefs().emplace_back(llvm::make_unique<WriteState>(
- WD, RegID, /* ClearsSuperRegs */ WriteMask[WriteIndex]));
- ++WriteIndex;
- }
-
- return NewIS;
-}
-} // namespace mca
diff --git a/tools/llvm-mca/InstrBuilder.h b/tools/llvm-mca/InstrBuilder.h
deleted file mode 100644
index 69a53b6fec21..000000000000
--- a/tools/llvm-mca/InstrBuilder.h
+++ /dev/null
@@ -1,85 +0,0 @@
-//===--------------------- InstrBuilder.h -----------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// A builder class for instructions that are statically analyzed by llvm-mca.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_INSTRBUILDER_H
-#define LLVM_TOOLS_LLVM_MCA_INSTRBUILDER_H
-
-#include "Instruction.h"
-#include "Support.h"
-#include "llvm/MC/MCInstPrinter.h"
-#include "llvm/MC/MCInstrAnalysis.h"
-#include "llvm/MC/MCInstrInfo.h"
-#include "llvm/MC/MCRegisterInfo.h"
-#include "llvm/MC/MCSubtargetInfo.h"
-
-namespace mca {
-
-class DispatchUnit;
-
-/// A builder class that knows how to construct Instruction objects.
-///
-/// Every llvm-mca Instruction is described by an object of class InstrDesc.
-/// An InstrDesc describes which registers are read/written by the instruction,
-/// as well as the instruction latency and hardware resources consumed.
-///
-/// This class is used by the tool to construct Instructions and instruction
-/// descriptors (i.e. InstrDesc objects).
-/// Information from the machine scheduling model is used to identify processor
-/// resources that are consumed by an instruction.
-class InstrBuilder {
- const llvm::MCSubtargetInfo &STI;
- const llvm::MCInstrInfo &MCII;
- const llvm::MCRegisterInfo &MRI;
- const llvm::MCInstrAnalysis &MCIA;
- llvm::MCInstPrinter &MCIP;
- llvm::SmallVector<uint64_t, 8> ProcResourceMasks;
-
- llvm::DenseMap<unsigned short, std::unique_ptr<const InstrDesc>> Descriptors;
- llvm::DenseMap<const llvm::MCInst *, std::unique_ptr<const InstrDesc>>
- VariantDescriptors;
-
- const InstrDesc &createInstrDescImpl(const llvm::MCInst &MCI);
- InstrBuilder(const InstrBuilder &) = delete;
- InstrBuilder &operator=(const InstrBuilder &) = delete;
-
- void populateWrites(InstrDesc &ID, const llvm::MCInst &MCI,
- unsigned SchedClassID);
- void populateReads(InstrDesc &ID, const llvm::MCInst &MCI,
- unsigned SchedClassID);
-
-public:
- InstrBuilder(const llvm::MCSubtargetInfo &sti, const llvm::MCInstrInfo &mcii,
- const llvm::MCRegisterInfo &mri,
- const llvm::MCInstrAnalysis &mcia, llvm::MCInstPrinter &mcip)
- : STI(sti), MCII(mcii), MRI(mri), MCIA(mcia), MCIP(mcip),
- ProcResourceMasks(STI.getSchedModel().getNumProcResourceKinds()) {
- computeProcResourceMasks(STI.getSchedModel(), ProcResourceMasks);
- }
-
- const InstrDesc &getOrCreateInstrDesc(const llvm::MCInst &MCI);
- // Returns an array of processor resource masks.
- // Masks are computed by function mca::computeProcResourceMasks. see
- // Support.h for a description of how masks are computed and how masks can be
- // used to solve set membership problems.
- llvm::ArrayRef<uint64_t> getProcResourceMasks() const {
- return ProcResourceMasks;
- }
-
- void clear() { VariantDescriptors.shrink_and_clear(); }
-
- std::unique_ptr<Instruction> createInstruction(const llvm::MCInst &MCI);
-};
-} // namespace mca
-
-#endif
diff --git a/tools/llvm-mca/Instruction.cpp b/tools/llvm-mca/Instruction.cpp
deleted file mode 100644
index 0c8476705572..000000000000
--- a/tools/llvm-mca/Instruction.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-//===--------------------- Instruction.cpp ----------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines abstractions used by the Pipeline to model register reads,
-// register writes and instructions.
-//
-//===----------------------------------------------------------------------===//
-
-#include "Instruction.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/raw_ostream.h"
-
-namespace mca {
-
-using namespace llvm;
-
-void ReadState::writeStartEvent(unsigned Cycles) {
- assert(DependentWrites);
- assert(CyclesLeft == UNKNOWN_CYCLES);
-
- // This read may be dependent on more than one write. This typically occurs
- // when a definition is the result of multiple writes where at least one
- // write does a partial register update.
- // The HW is forced to do some extra bookkeeping to track of all the
- // dependent writes, and implement a merging scheme for the partial writes.
- --DependentWrites;
- TotalCycles = std::max(TotalCycles, Cycles);
-
- if (!DependentWrites) {
- CyclesLeft = TotalCycles;
- IsReady = !CyclesLeft;
- }
-}
-
-void WriteState::onInstructionIssued() {
- assert(CyclesLeft == UNKNOWN_CYCLES);
- // Update the number of cycles left based on the WriteDescriptor info.
- CyclesLeft = getLatency();
-
- // Now that the time left before write-back is known, notify
- // all the users.
- for (const std::pair<ReadState *, int> &User : Users) {
- ReadState *RS = User.first;
- unsigned ReadCycles = std::max(0, CyclesLeft - User.second);
- RS->writeStartEvent(ReadCycles);
- }
-}
-
-void WriteState::addUser(ReadState *User, int ReadAdvance) {
- // If CyclesLeft is different than -1, then we don't need to
- // update the list of users. We can just notify the user with
- // the actual number of cycles left (which may be zero).
- if (CyclesLeft != UNKNOWN_CYCLES) {
- unsigned ReadCycles = std::max(0, CyclesLeft - ReadAdvance);
- User->writeStartEvent(ReadCycles);
- return;
- }
-
- std::pair<ReadState *, int> NewPair(User, ReadAdvance);
- Users.insert(NewPair);
-}
-
-void WriteState::cycleEvent() {
- // Note: CyclesLeft can be a negative number. It is an error to
- // make it an unsigned quantity because users of this write may
- // specify a negative ReadAdvance.
- if (CyclesLeft != UNKNOWN_CYCLES)
- CyclesLeft--;
-}
-
-void ReadState::cycleEvent() {
- // Update the total number of cycles.
- if (DependentWrites && TotalCycles) {
- --TotalCycles;
- return;
- }
-
- // Bail out immediately if we don't know how many cycles are left.
- if (CyclesLeft == UNKNOWN_CYCLES)
- return;
-
- if (CyclesLeft) {
- --CyclesLeft;
- IsReady = !CyclesLeft;
- }
-}
-
-#ifndef NDEBUG
-void WriteState::dump() const {
- dbgs() << "{ OpIdx=" << WD.OpIndex << ", Lat=" << getLatency() << ", RegID "
- << getRegisterID() << ", Cycles Left=" << getCyclesLeft() << " }";
-}
-
-void WriteRef::dump() const {
- dbgs() << "IID=" << getSourceIndex() << ' ';
- if (isValid())
- getWriteState()->dump();
- else
- dbgs() << "(null)";
-}
-#endif
-
-void Instruction::dispatch(unsigned RCUToken) {
- assert(Stage == IS_INVALID);
- Stage = IS_AVAILABLE;
- RCUTokenID = RCUToken;
-
- // Check if input operands are already available.
- update();
-}
-
-void Instruction::execute() {
- assert(Stage == IS_READY);
- Stage = IS_EXECUTING;
-
- // Set the cycles left before the write-back stage.
- CyclesLeft = Desc.MaxLatency;
-
- for (UniqueDef &Def : Defs)
- Def->onInstructionIssued();
-
- // Transition to the "executed" stage if this is a zero-latency instruction.
- if (!CyclesLeft)
- Stage = IS_EXECUTED;
-}
-
-void Instruction::update() {
- assert(isDispatched() && "Unexpected instruction stage found!");
-
- if (!llvm::all_of(Uses, [](const UniqueUse &Use) { return Use->isReady(); }))
- return;
-
- // A partial register write cannot complete before a dependent write.
- auto IsDefReady = [&](const UniqueDef &Def) {
- if (const WriteState *Write = Def->getDependentWrite()) {
- int WriteLatency = Write->getCyclesLeft();
- if (WriteLatency == UNKNOWN_CYCLES)
- return false;
- return static_cast<unsigned>(WriteLatency) < Desc.MaxLatency;
- }
- return true;
- };
-
- if (llvm::all_of(Defs, IsDefReady))
- Stage = IS_READY;
-}
-
-void Instruction::cycleEvent() {
- if (isReady())
- return;
-
- if (isDispatched()) {
- for (UniqueUse &Use : Uses)
- Use->cycleEvent();
-
- update();
- return;
- }
-
- assert(isExecuting() && "Instruction not in-flight?");
- assert(CyclesLeft && "Instruction already executed?");
- for (UniqueDef &Def : Defs)
- Def->cycleEvent();
- CyclesLeft--;
- if (!CyclesLeft)
- Stage = IS_EXECUTED;
-}
-
-const unsigned WriteRef::INVALID_IID = std::numeric_limits<unsigned>::max();
-
-} // namespace mca
diff --git a/tools/llvm-mca/Instruction.h b/tools/llvm-mca/Instruction.h
deleted file mode 100644
index 3b2f90528f2e..000000000000
--- a/tools/llvm-mca/Instruction.h
+++ /dev/null
@@ -1,434 +0,0 @@
-//===--------------------- Instruction.h ------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines abstractions used by the Pipeline to model register reads,
-/// register writes and instructions.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_INSTRUCTION_H
-#define LLVM_TOOLS_LLVM_MCA_INSTRUCTION_H
-
-#include "llvm/Support/MathExtras.h"
-
-#ifndef NDEBUG
-#include "llvm/Support/raw_ostream.h"
-#endif
-
-#include <memory>
-#include <set>
-#include <vector>
-
-namespace mca {
-
-constexpr int UNKNOWN_CYCLES = -512;
-
-/// A register write descriptor.
-struct WriteDescriptor {
- // Operand index. The index is negative for implicit writes only.
- // For implicit writes, the actual operand index is computed performing
- // a bitwise not of the OpIndex.
- int OpIndex;
- // Write latency. Number of cycles before write-back stage.
- unsigned Latency;
- // This field is set to a value different than zero only if this
- // is an implicit definition.
- unsigned RegisterID;
- // Instruction itineraries would set this field to the SchedClass ID.
- // Otherwise, it defaults to the WriteResourceID from the MCWriteLatencyEntry
- // element associated to this write.
- // When computing read latencies, this value is matched against the
- // "ReadAdvance" information. The hardware backend may implement
- // dedicated forwarding paths to quickly propagate write results to dependent
- // instructions waiting in the reservation station (effectively bypassing the
- // write-back stage).
- unsigned SClassOrWriteResourceID;
- // True only if this is a write obtained from an optional definition.
- // Optional definitions are allowed to reference regID zero (i.e. "no
- // register").
- bool IsOptionalDef;
-
- bool isImplicitWrite() const { return OpIndex < 0; };
-};
-
-/// A register read descriptor.
-struct ReadDescriptor {
- // A MCOperand index. This is used by the Dispatch logic to identify register
- // reads. Implicit reads have negative indices. The actual operand index of an
- // implicit read is the bitwise not of field OpIndex.
- int OpIndex;
- // The actual "UseIdx". This is used to query the ReadAdvance table. Explicit
- // uses always come first in the sequence of uses.
- unsigned UseIndex;
- // This field is only set if this is an implicit read.
- unsigned RegisterID;
- // Scheduling Class Index. It is used to query the scheduling model for the
- // MCSchedClassDesc object.
- unsigned SchedClassID;
-
- bool isImplicitRead() const { return OpIndex < 0; };
-};
-
-class ReadState;
-
-/// Tracks uses of a register definition (e.g. register write).
-///
-/// Each implicit/explicit register write is associated with an instance of
-/// this class. A WriteState object tracks the dependent users of a
-/// register write. It also tracks how many cycles are left before the write
-/// back stage.
-class WriteState {
- const WriteDescriptor &WD;
- // On instruction issue, this field is set equal to the write latency.
- // Before instruction issue, this field defaults to -512, a special
- // value that represents an "unknown" number of cycles.
- int CyclesLeft;
-
- // Actual register defined by this write. This field is only used
- // to speedup queries on the register file.
- // For implicit writes, this field always matches the value of
- // field RegisterID from WD.
- unsigned RegisterID;
-
- // True if this write implicitly clears the upper portion of RegisterID's
- // super-registers.
- bool ClearsSuperRegs;
-
- // This field is set if this is a partial register write, and it has a false
- // dependency on any previous write of the same register (or a portion of it).
- // DependentWrite must be able to complete before this write completes, so
- // that we don't break the WAW, and the two writes can be merged together.
- const WriteState *DependentWrite;
-
- // A list of dependent reads. Users is a set of dependent
- // reads. A dependent read is added to the set only if CyclesLeft
- // is "unknown". As soon as CyclesLeft is 'known', each user in the set
- // gets notified with the actual CyclesLeft.
-
- // The 'second' element of a pair is a "ReadAdvance" number of cycles.
- std::set<std::pair<ReadState *, int>> Users;
-
-public:
- WriteState(const WriteDescriptor &Desc, unsigned RegID,
- bool clearsSuperRegs = false)
- : WD(Desc), CyclesLeft(UNKNOWN_CYCLES), RegisterID(RegID),
- ClearsSuperRegs(clearsSuperRegs), DependentWrite(nullptr) {}
- WriteState(const WriteState &Other) = delete;
- WriteState &operator=(const WriteState &Other) = delete;
-
- int getCyclesLeft() const { return CyclesLeft; }
- unsigned getWriteResourceID() const { return WD.SClassOrWriteResourceID; }
- unsigned getRegisterID() const { return RegisterID; }
- unsigned getLatency() const { return WD.Latency; }
-
- void addUser(ReadState *Use, int ReadAdvance);
- unsigned getNumUsers() const { return Users.size(); }
- bool clearsSuperRegisters() const { return ClearsSuperRegs; }
-
- const WriteState *getDependentWrite() const { return DependentWrite; }
- void setDependentWrite(const WriteState *Write) { DependentWrite = Write; }
-
- // On every cycle, update CyclesLeft and notify dependent users.
- void cycleEvent();
- void onInstructionIssued();
-
-#ifndef NDEBUG
- void dump() const;
-#endif
-};
-
-/// Tracks register operand latency in cycles.
-///
-/// A read may be dependent on more than one write. This occurs when some
-/// writes only partially update the register associated to this read.
-class ReadState {
- const ReadDescriptor &RD;
- // Physical register identified associated to this read.
- unsigned RegisterID;
- // Number of writes that contribute to the definition of RegisterID.
- // In the absence of partial register updates, the number of DependentWrites
- // cannot be more than one.
- unsigned DependentWrites;
- // Number of cycles left before RegisterID can be read. This value depends on
- // the latency of all the dependent writes. It defaults to UNKNOWN_CYCLES.
- // It gets set to the value of field TotalCycles only when the 'CyclesLeft' of
- // every dependent write is known.
- int CyclesLeft;
- // This field is updated on every writeStartEvent(). When the number of
- // dependent writes (i.e. field DependentWrite) is zero, this value is
- // propagated to field CyclesLeft.
- unsigned TotalCycles;
- // This field is set to true only if there are no dependent writes, and
- // there are no `CyclesLeft' to wait.
- bool IsReady;
-
-public:
- ReadState(const ReadDescriptor &Desc, unsigned RegID)
- : RD(Desc), RegisterID(RegID), DependentWrites(0),
- CyclesLeft(UNKNOWN_CYCLES), TotalCycles(0), IsReady(true) {}
- ReadState(const ReadState &Other) = delete;
- ReadState &operator=(const ReadState &Other) = delete;
-
- const ReadDescriptor &getDescriptor() const { return RD; }
- unsigned getSchedClass() const { return RD.SchedClassID; }
- unsigned getRegisterID() const { return RegisterID; }
-
- bool isReady() const { return IsReady; }
- bool isImplicitRead() const { return RD.isImplicitRead(); }
-
- void cycleEvent();
- void writeStartEvent(unsigned Cycles);
- void setDependentWrites(unsigned Writes) {
- DependentWrites = Writes;
- IsReady = !Writes;
- }
-};
-
-/// A sequence of cycles.
-///
-/// This class can be used as a building block to construct ranges of cycles.
-class CycleSegment {
- unsigned Begin; // Inclusive.
- unsigned End; // Exclusive.
- bool Reserved; // Resources associated to this segment must be reserved.
-
-public:
- CycleSegment(unsigned StartCycle, unsigned EndCycle, bool IsReserved = false)
- : Begin(StartCycle), End(EndCycle), Reserved(IsReserved) {}
-
- bool contains(unsigned Cycle) const { return Cycle >= Begin && Cycle < End; }
- bool startsAfter(const CycleSegment &CS) const { return End <= CS.Begin; }
- bool endsBefore(const CycleSegment &CS) const { return Begin >= CS.End; }
- bool overlaps(const CycleSegment &CS) const {
- return !startsAfter(CS) && !endsBefore(CS);
- }
- bool isExecuting() const { return Begin == 0 && End != 0; }
- bool isExecuted() const { return End == 0; }
- bool operator<(const CycleSegment &Other) const {
- return Begin < Other.Begin;
- }
- CycleSegment &operator--(void) {
- if (Begin)
- Begin--;
- if (End)
- End--;
- return *this;
- }
-
- bool isValid() const { return Begin <= End; }
- unsigned size() const { return End - Begin; };
- void Subtract(unsigned Cycles) {
- assert(End >= Cycles);
- End -= Cycles;
- }
-
- unsigned begin() const { return Begin; }
- unsigned end() const { return End; }
- void setEnd(unsigned NewEnd) { End = NewEnd; }
- bool isReserved() const { return Reserved; }
- void setReserved() { Reserved = true; }
-};
-
-/// Helper used by class InstrDesc to describe how hardware resources
-/// are used.
-///
-/// This class describes how many resource units of a specific resource kind
-/// (and how many cycles) are "used" by an instruction.
-struct ResourceUsage {
- CycleSegment CS;
- unsigned NumUnits;
- ResourceUsage(CycleSegment Cycles, unsigned Units = 1)
- : CS(Cycles), NumUnits(Units) {}
- unsigned size() const { return CS.size(); }
- bool isReserved() const { return CS.isReserved(); }
- void setReserved() { CS.setReserved(); }
-};
-
-/// An instruction descriptor
-struct InstrDesc {
- std::vector<WriteDescriptor> Writes; // Implicit writes are at the end.
- std::vector<ReadDescriptor> Reads; // Implicit reads are at the end.
-
- // For every resource used by an instruction of this kind, this vector
- // reports the number of "consumed cycles".
- std::vector<std::pair<uint64_t, ResourceUsage>> Resources;
-
- // A list of buffered resources consumed by this instruction.
- std::vector<uint64_t> Buffers;
- unsigned MaxLatency;
- // Number of MicroOps for this instruction.
- unsigned NumMicroOps;
-
- bool MayLoad;
- bool MayStore;
- bool HasSideEffects;
-
- // A zero latency instruction doesn't consume any scheduler resources.
- bool isZeroLatency() const { return !MaxLatency && Resources.empty(); }
-};
-
-/// An instruction propagated through the simulated instruction pipeline.
-///
-/// This class is used to monitor changes to the internal state of instructions
-/// that are sent to the various components of the simulated hardware pipeline.
-class Instruction {
- const InstrDesc &Desc;
-
- enum InstrStage {
- IS_INVALID, // Instruction in an invalid state.
- IS_AVAILABLE, // Instruction dispatched but operands are not ready.
- IS_READY, // Instruction dispatched and operands ready.
- IS_EXECUTING, // Instruction issued.
- IS_EXECUTED, // Instruction executed. Values are written back.
- IS_RETIRED // Instruction retired.
- };
-
- // The current instruction stage.
- enum InstrStage Stage;
-
- // This value defaults to the instruction latency. This instruction is
- // considered executed when field CyclesLeft goes to zero.
- int CyclesLeft;
-
- // Retire Unit token ID for this instruction.
- unsigned RCUTokenID;
-
- bool IsDepBreaking;
-
- using UniqueDef = std::unique_ptr<WriteState>;
- using UniqueUse = std::unique_ptr<ReadState>;
- using VecDefs = std::vector<UniqueDef>;
- using VecUses = std::vector<UniqueUse>;
-
- // Output dependencies.
- // One entry per each implicit and explicit register definition.
- VecDefs Defs;
-
- // Input dependencies.
- // One entry per each implicit and explicit register use.
- VecUses Uses;
-
-public:
- Instruction(const InstrDesc &D)
- : Desc(D), Stage(IS_INVALID), CyclesLeft(UNKNOWN_CYCLES), RCUTokenID(0),
- IsDepBreaking(false) {}
- Instruction(const Instruction &Other) = delete;
- Instruction &operator=(const Instruction &Other) = delete;
-
- VecDefs &getDefs() { return Defs; }
- const VecDefs &getDefs() const { return Defs; }
- VecUses &getUses() { return Uses; }
- const VecUses &getUses() const { return Uses; }
- const InstrDesc &getDesc() const { return Desc; }
- unsigned getRCUTokenID() const { return RCUTokenID; }
- int getCyclesLeft() const { return CyclesLeft; }
-
- bool isDependencyBreaking() const { return IsDepBreaking; }
- void setDependencyBreaking() { IsDepBreaking = true; }
-
- unsigned getNumUsers() const {
- unsigned NumUsers = 0;
- for (const UniqueDef &Def : Defs)
- NumUsers += Def->getNumUsers();
- return NumUsers;
- }
-
- // Transition to the dispatch stage, and assign a RCUToken to this
- // instruction. The RCUToken is used to track the completion of every
- // register write performed by this instruction.
- void dispatch(unsigned RCUTokenID);
-
- // Instruction issued. Transition to the IS_EXECUTING state, and update
- // all the definitions.
- void execute();
-
- // Force a transition from the IS_AVAILABLE state to the IS_READY state if
- // input operands are all ready. State transitions normally occur at the
- // beginning of a new cycle (see method cycleEvent()). However, the scheduler
- // may decide to promote instructions from the wait queue to the ready queue
- // as the result of another issue event. This method is called every time the
- // instruction might have changed in state.
- void update();
-
- bool isDispatched() const { return Stage == IS_AVAILABLE; }
- bool isReady() const { return Stage == IS_READY; }
- bool isExecuting() const { return Stage == IS_EXECUTING; }
- bool isExecuted() const { return Stage == IS_EXECUTED; }
- bool isRetired() const { return Stage == IS_RETIRED; }
-
- void retire() {
- assert(isExecuted() && "Instruction is in an invalid state!");
- Stage = IS_RETIRED;
- }
-
- void cycleEvent();
-};
-
-/// An InstRef contains both a SourceMgr index and Instruction pair. The index
-/// is used as a unique identifier for the instruction. MCA will make use of
-/// this index as a key throughout MCA.
-class InstRef : public std::pair<unsigned, Instruction *> {
-public:
- InstRef() : std::pair<unsigned, Instruction *>(0, nullptr) {}
- InstRef(unsigned Index, Instruction *I)
- : std::pair<unsigned, Instruction *>(Index, I) {}
-
- unsigned getSourceIndex() const { return first; }
- Instruction *getInstruction() { return second; }
- const Instruction *getInstruction() const { return second; }
-
- /// Returns true if this InstRef has been populated.
- bool isValid() const { return second != nullptr; }
-
-#ifndef NDEBUG
- void print(llvm::raw_ostream &OS) const { OS << getSourceIndex(); }
-#endif
-};
-
-#ifndef NDEBUG
-inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const InstRef &IR) {
- IR.print(OS);
- return OS;
-}
-#endif
-
-/// A reference to a register write.
-///
-/// This class is mainly used by the register file to describe register
-/// mappings. It correlates a register write to the source index of the
-/// defining instruction.
-class WriteRef {
- std::pair<unsigned, WriteState *> Data;
- static const unsigned INVALID_IID;
-
-public:
- WriteRef() : Data(INVALID_IID, nullptr) {}
- WriteRef(unsigned SourceIndex, WriteState *WS) : Data(SourceIndex, WS) {}
-
- unsigned getSourceIndex() const { return Data.first; }
- const WriteState *getWriteState() const { return Data.second; }
- WriteState *getWriteState() { return Data.second; }
- void invalidate() { Data = std::make_pair(INVALID_IID, nullptr); }
-
- bool isValid() const {
- return Data.first != INVALID_IID && Data.second != nullptr;
- }
- bool operator==(const WriteRef &Other) const {
- return Data == Other.Data;
- }
-
-#ifndef NDEBUG
- void dump() const;
-#endif
-};
-
-} // namespace mca
-
-#endif
diff --git a/tools/llvm-mca/InstructionTables.cpp b/tools/llvm-mca/InstructionTables.cpp
deleted file mode 100644
index 9b9dbc37fbdb..000000000000
--- a/tools/llvm-mca/InstructionTables.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-//===--------------------- InstructionTables.cpp ----------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file implements the method InstructionTables::execute().
-/// Method execute() prints a theoretical resource pressure distribution based
-/// on the information available in the scheduling model, and without running
-/// the pipeline.
-///
-//===----------------------------------------------------------------------===//
-
-#include "InstructionTables.h"
-
-namespace mca {
-
-using namespace llvm;
-
-bool InstructionTables::execute(InstRef &IR) {
- ArrayRef<uint64_t> Masks = IB.getProcResourceMasks();
- const InstrDesc &Desc = IR.getInstruction()->getDesc();
- UsedResources.clear();
-
- // Identify the resources consumed by this instruction.
- for (const std::pair<uint64_t, ResourceUsage> Resource : Desc.Resources) {
- // Skip zero-cycle resources (i.e., unused resources).
- if (!Resource.second.size())
- continue;
- double Cycles = static_cast<double>(Resource.second.size());
- unsigned Index = std::distance(
- Masks.begin(), std::find(Masks.begin(), Masks.end(), Resource.first));
- const MCProcResourceDesc &ProcResource = *SM.getProcResource(Index);
- unsigned NumUnits = ProcResource.NumUnits;
- if (!ProcResource.SubUnitsIdxBegin) {
- // The number of cycles consumed by each unit.
- Cycles /= NumUnits;
- for (unsigned I = 0, E = NumUnits; I < E; ++I) {
- ResourceRef ResourceUnit = std::make_pair(Index, 1U << I);
- UsedResources.emplace_back(std::make_pair(ResourceUnit, Cycles));
- }
- continue;
- }
-
- // This is a group. Obtain the set of resources contained in this
- // group. Some of these resources may implement multiple units.
- // Uniformly distribute Cycles across all of the units.
- for (unsigned I1 = 0; I1 < NumUnits; ++I1) {
- unsigned SubUnitIdx = ProcResource.SubUnitsIdxBegin[I1];
- const MCProcResourceDesc &SubUnit = *SM.getProcResource(SubUnitIdx);
- // Compute the number of cycles consumed by each resource unit.
- double RUCycles = Cycles / (NumUnits * SubUnit.NumUnits);
- for (unsigned I2 = 0, E2 = SubUnit.NumUnits; I2 < E2; ++I2) {
- ResourceRef ResourceUnit = std::make_pair(SubUnitIdx, 1U << I2);
- UsedResources.emplace_back(std::make_pair(ResourceUnit, RUCycles));
- }
- }
- }
-
- // Send a fake instruction issued event to all the views.
- HWInstructionIssuedEvent Event(IR, UsedResources);
- notifyEvent<HWInstructionIssuedEvent>(Event);
- return true;
-}
-
-} // namespace mca
diff --git a/tools/llvm-mca/InstructionTables.h b/tools/llvm-mca/InstructionTables.h
deleted file mode 100644
index 18e019988430..000000000000
--- a/tools/llvm-mca/InstructionTables.h
+++ /dev/null
@@ -1,43 +0,0 @@
-//===--------------------- InstructionTables.h ------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file implements a custom stage to generate instruction tables.
-/// See the description of command-line flag -instruction-tables in
-/// docs/CommandGuide/lvm-mca.rst
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_INSTRUCTIONTABLES_H
-#define LLVM_TOOLS_LLVM_MCA_INSTRUCTIONTABLES_H
-
-#include "InstrBuilder.h"
-#include "Scheduler.h"
-#include "Stage.h"
-#include "View.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/MC/MCSchedule.h"
-
-namespace mca {
-
-class InstructionTables : public Stage {
- const llvm::MCSchedModel &SM;
- InstrBuilder &IB;
- llvm::SmallVector<std::pair<ResourceRef, double>, 4> UsedResources;
-
-public:
- InstructionTables(const llvm::MCSchedModel &Model, InstrBuilder &Builder)
- : Stage(), SM(Model), IB(Builder) {}
-
- bool hasWorkToComplete() const override final { return false; }
- bool execute(InstRef &IR) override final;
-};
-} // namespace mca
-
-#endif
diff --git a/tools/llvm-mca/LLVMBuild.txt b/tools/llvm-mca/LLVMBuild.txt
index 0afcd3129ecd..a704612934ff 100644
--- a/tools/llvm-mca/LLVMBuild.txt
+++ b/tools/llvm-mca/LLVMBuild.txt
@@ -19,4 +19,4 @@
type = Tool
name = llvm-mca
parent = Tools
-required_libraries = MC MCParser Support all-targets
+required_libraries = MC MCA MCParser Support all-targets
diff --git a/tools/llvm-mca/LSUnit.cpp b/tools/llvm-mca/LSUnit.cpp
deleted file mode 100644
index 9ee3b6171893..000000000000
--- a/tools/llvm-mca/LSUnit.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-//===----------------------- LSUnit.cpp --------------------------*- C++-*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// A Load-Store Unit for the llvm-mca tool.
-///
-//===----------------------------------------------------------------------===//
-
-#include "LSUnit.h"
-#include "Instruction.h"
-
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/raw_ostream.h"
-
-using namespace llvm;
-
-#define DEBUG_TYPE "llvm-mca"
-
-namespace mca {
-
-#ifndef NDEBUG
-void LSUnit::dump() const {
- dbgs() << "[LSUnit] LQ_Size = " << LQ_Size << '\n';
- dbgs() << "[LSUnit] SQ_Size = " << SQ_Size << '\n';
- dbgs() << "[LSUnit] NextLQSlotIdx = " << LoadQueue.size() << '\n';
- dbgs() << "[LSUnit] NextSQSlotIdx = " << StoreQueue.size() << '\n';
-}
-#endif
-
-void LSUnit::assignLQSlot(unsigned Index) {
- assert(!isLQFull());
- assert(LoadQueue.count(Index) == 0);
-
- LLVM_DEBUG(dbgs() << "[LSUnit] - AssignLQSlot <Idx=" << Index
- << ",slot=" << LoadQueue.size() << ">\n");
- LoadQueue.insert(Index);
-}
-
-void LSUnit::assignSQSlot(unsigned Index) {
- assert(!isSQFull());
- assert(StoreQueue.count(Index) == 0);
-
- LLVM_DEBUG(dbgs() << "[LSUnit] - AssignSQSlot <Idx=" << Index
- << ",slot=" << StoreQueue.size() << ">\n");
- StoreQueue.insert(Index);
-}
-
-bool LSUnit::reserve(const InstRef &IR) {
- const InstrDesc &Desc = IR.getInstruction()->getDesc();
- unsigned MayLoad = Desc.MayLoad;
- unsigned MayStore = Desc.MayStore;
- unsigned IsMemBarrier = Desc.HasSideEffects;
- if (!MayLoad && !MayStore)
- return false;
-
- const unsigned Index = IR.getSourceIndex();
- if (MayLoad) {
- if (IsMemBarrier)
- LoadBarriers.insert(Index);
- assignLQSlot(Index);
- }
- if (MayStore) {
- if (IsMemBarrier)
- StoreBarriers.insert(Index);
- assignSQSlot(Index);
- }
- return true;
-}
-
-bool LSUnit::isReady(const InstRef &IR) const {
- const unsigned Index = IR.getSourceIndex();
- bool IsALoad = LoadQueue.count(Index) != 0;
- bool IsAStore = StoreQueue.count(Index) != 0;
- assert((IsALoad || IsAStore) && "Instruction is not in queue!");
-
- if (IsALoad && !LoadBarriers.empty()) {
- unsigned LoadBarrierIndex = *LoadBarriers.begin();
- if (Index > LoadBarrierIndex)
- return false;
- if (Index == LoadBarrierIndex && Index != *LoadQueue.begin())
- return false;
- }
-
- if (IsAStore && !StoreBarriers.empty()) {
- unsigned StoreBarrierIndex = *StoreBarriers.begin();
- if (Index > StoreBarrierIndex)
- return false;
- if (Index == StoreBarrierIndex && Index != *StoreQueue.begin())
- return false;
- }
-
- if (NoAlias && IsALoad)
- return true;
-
- if (StoreQueue.size()) {
- // Check if this memory operation is younger than the older store.
- if (Index > *StoreQueue.begin())
- return false;
- }
-
- // Okay, we are older than the oldest store in the queue.
- // If there are no pending loads, then we can say for sure that this
- // instruction is ready.
- if (isLQEmpty())
- return true;
-
- // Check if there are no older loads.
- if (Index <= *LoadQueue.begin())
- return true;
-
- // There is at least one younger load.
- return !IsAStore;
-}
-
-void LSUnit::onInstructionExecuted(const InstRef &IR) {
- const unsigned Index = IR.getSourceIndex();
- std::set<unsigned>::iterator it = LoadQueue.find(Index);
- if (it != LoadQueue.end()) {
- LLVM_DEBUG(dbgs() << "[LSUnit]: Instruction idx=" << Index
- << " has been removed from the load queue.\n");
- LoadQueue.erase(it);
- }
-
- it = StoreQueue.find(Index);
- if (it != StoreQueue.end()) {
- LLVM_DEBUG(dbgs() << "[LSUnit]: Instruction idx=" << Index
- << " has been removed from the store queue.\n");
- StoreQueue.erase(it);
- }
-
- if (!StoreBarriers.empty() && Index == *StoreBarriers.begin()) {
- LLVM_DEBUG(dbgs() << "[LSUnit]: Instruction idx=" << Index
- << " has been removed from the set of store barriers.\n");
- StoreBarriers.erase(StoreBarriers.begin());
- }
- if (!LoadBarriers.empty() && Index == *LoadBarriers.begin()) {
- LLVM_DEBUG(dbgs() << "[LSUnit]: Instruction idx=" << Index
- << " has been removed from the set of load barriers.\n");
- LoadBarriers.erase(LoadBarriers.begin());
- }
-}
-} // namespace mca
diff --git a/tools/llvm-mca/LSUnit.h b/tools/llvm-mca/LSUnit.h
deleted file mode 100644
index 817522190589..000000000000
--- a/tools/llvm-mca/LSUnit.h
+++ /dev/null
@@ -1,147 +0,0 @@
-//===------------------------- LSUnit.h --------------------------*- C++-*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// A Load/Store unit class that models load/store queues and that implements
-/// a simple weak memory consistency model.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_LSUNIT_H
-#define LLVM_TOOLS_LLVM_MCA_LSUNIT_H
-
-#include <set>
-
-namespace mca {
-
-class InstRef;
-struct InstrDesc;
-
-/// A Load/Store Unit implementing a load and store queues.
-///
-/// This class implements a load queue and a store queue to emulate the
-/// out-of-order execution of memory operations.
-/// Each load (or store) consumes an entry in the load (or store) queue.
-///
-/// Rules are:
-/// 1) A younger load is allowed to pass an older load only if there are no
-/// stores nor barriers in between the two loads.
-/// 2) An younger store is not allowed to pass an older store.
-/// 3) A younger store is not allowed to pass an older load.
-/// 4) A younger load is allowed to pass an older store only if the load does
-/// not alias with the store.
-///
-/// This class optimistically assumes that loads don't alias store operations.
-/// Under this assumption, younger loads are always allowed to pass older
-/// stores (this would only affects rule 4).
-/// Essentially, this LSUnit doesn't attempt to run any sort alias analysis to
-/// predict when loads and stores don't alias with eachother.
-///
-/// To enforce aliasing between loads and stores, flag `AssumeNoAlias` must be
-/// set to `false` by the constructor of LSUnit.
-///
-/// In the case of write-combining memory, rule 2. could be relaxed to allow
-/// reordering of non-aliasing store operations. At the moment, this is not
-/// allowed.
-/// To put it in another way, there is no option to specify a different memory
-/// type for memory operations (example: write-through, write-combining, etc.).
-/// Also, there is no way to weaken the memory model, and this unit currently
-/// doesn't support write-combining behavior.
-///
-/// No assumptions are made on the size of the store buffer.
-/// As mentioned before, this class doesn't perform alias analysis.
-/// Consequently, LSUnit doesn't know how to identify cases where
-/// store-to-load forwarding may occur.
-///
-/// LSUnit doesn't attempt to predict whether a load or store hits or misses
-/// the L1 cache. To be more specific, LSUnit doesn't know anything about
-/// the cache hierarchy and memory types.
-/// It only knows if an instruction "mayLoad" and/or "mayStore". For loads, the
-/// scheduling model provides an "optimistic" load-to-use latency (which usually
-/// matches the load-to-use latency for when there is a hit in the L1D).
-///
-/// Class MCInstrDesc in LLVM doesn't know about serializing operations, nor
-/// memory-barrier like instructions.
-/// LSUnit conservatively assumes that an instruction which `mayLoad` and has
-/// `unmodeled side effects` behave like a "soft" load-barrier. That means, it
-/// serializes loads without forcing a flush of the load queue.
-/// Similarly, instructions that both `mayStore` and have `unmodeled side
-/// effects` are treated like store barriers. A full memory
-/// barrier is a 'mayLoad' and 'mayStore' instruction with unmodeled side
-/// effects. This is obviously inaccurate, but this is the best that we can do
-/// at the moment.
-///
-/// Each load/store barrier consumes one entry in the load/store queue. A
-/// load/store barrier enforces ordering of loads/stores:
-/// - A younger load cannot pass a load barrier.
-/// - A younger store cannot pass a store barrier.
-///
-/// A younger load has to wait for the memory load barrier to execute.
-/// A load/store barrier is "executed" when it becomes the oldest entry in
-/// the load/store queue(s). That also means, all the older loads/stores have
-/// already been executed.
-class LSUnit {
- // Load queue size.
- // LQ_Size == 0 means that there are infinite slots in the load queue.
- unsigned LQ_Size;
-
- // Store queue size.
- // SQ_Size == 0 means that there are infinite slots in the store queue.
- unsigned SQ_Size;
-
- // If true, loads will never alias with stores. This is the default.
- bool NoAlias;
-
- std::set<unsigned> LoadQueue;
- std::set<unsigned> StoreQueue;
-
- void assignLQSlot(unsigned Index);
- void assignSQSlot(unsigned Index);
- bool isReadyNoAlias(unsigned Index) const;
-
- // An instruction that both 'mayStore' and 'HasUnmodeledSideEffects' is
- // conservatively treated as a store barrier. It forces older store to be
- // executed before newer stores are issued.
- std::set<unsigned> StoreBarriers;
-
- // An instruction that both 'MayLoad' and 'HasUnmodeledSideEffects' is
- // conservatively treated as a load barrier. It forces older loads to execute
- // before newer loads are issued.
- std::set<unsigned> LoadBarriers;
-
-public:
- LSUnit(unsigned LQ = 0, unsigned SQ = 0, bool AssumeNoAlias = false)
- : LQ_Size(LQ), SQ_Size(SQ), NoAlias(AssumeNoAlias) {}
-
-#ifndef NDEBUG
- void dump() const;
-#endif
-
- bool isSQEmpty() const { return StoreQueue.empty(); }
- bool isLQEmpty() const { return LoadQueue.empty(); }
- bool isSQFull() const { return SQ_Size != 0 && StoreQueue.size() == SQ_Size; }
- bool isLQFull() const { return LQ_Size != 0 && LoadQueue.size() == LQ_Size; }
-
- // Returns true if this instruction has been successfully enqueued.
- bool reserve(const InstRef &IR);
-
- // The rules are:
- // 1. A store may not pass a previous store.
- // 2. A load may not pass a previous store unless flag 'NoAlias' is set.
- // 3. A load may pass a previous load.
- // 4. A store may not pass a previous load (regardless of flag 'NoAlias').
- // 5. A load has to wait until an older load barrier is fully executed.
- // 6. A store has to wait until an older store barrier is fully executed.
- bool isReady(const InstRef &IR) const;
- void onInstructionExecuted(const InstRef &IR);
-};
-
-} // namespace mca
-
-#endif
diff --git a/tools/llvm-mca/Pipeline.cpp b/tools/llvm-mca/Pipeline.cpp
deleted file mode 100644
index 7c937e7b48b5..000000000000
--- a/tools/llvm-mca/Pipeline.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-//===--------------------- Pipeline.cpp -------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file implements an ordered container of stages that simulate the
-/// pipeline of a hardware backend.
-///
-//===----------------------------------------------------------------------===//
-
-#include "Pipeline.h"
-#include "HWEventListener.h"
-#include "llvm/CodeGen/TargetSchedule.h"
-#include "llvm/Support/Debug.h"
-
-namespace mca {
-
-#define DEBUG_TYPE "llvm-mca"
-
-using namespace llvm;
-
-void Pipeline::addEventListener(HWEventListener *Listener) {
- if (Listener)
- Listeners.insert(Listener);
- for (auto &S : Stages)
- S->addListener(Listener);
-}
-
-bool Pipeline::hasWorkToProcess() {
- const auto It = llvm::find_if(Stages, [](const std::unique_ptr<Stage> &S) {
- return S->hasWorkToComplete();
- });
- return It != Stages.end();
-}
-
-// This routine returns early if any stage returns 'false' after execute() is
-// called on it.
-bool Pipeline::executeStages(InstRef &IR) {
- for (const std::unique_ptr<Stage> &S : Stages)
- if (!S->execute(IR))
- return false;
- return true;
-}
-
-void Pipeline::preExecuteStages() {
- for (const std::unique_ptr<Stage> &S : Stages)
- S->preExecute();
-}
-
-void Pipeline::postExecuteStages() {
- for (const std::unique_ptr<Stage> &S : Stages)
- S->postExecute();
-}
-
-void Pipeline::run() {
- while (hasWorkToProcess()) {
- notifyCycleBegin();
- runCycle();
- notifyCycleEnd();
- ++Cycles;
- }
-}
-
-void Pipeline::runCycle() {
- // Update the stages before we do any processing for this cycle.
- InstRef IR;
- for (auto &S : Stages)
- S->cycleStart();
-
- // Continue executing this cycle until any stage claims it cannot make
- // progress.
- while (true) {
- preExecuteStages();
- if (!executeStages(IR))
- break;
- postExecuteStages();
- }
-
- for (auto &S : Stages)
- S->cycleEnd();
-}
-
-void Pipeline::notifyCycleBegin() {
- LLVM_DEBUG(dbgs() << "[E] Cycle begin: " << Cycles << '\n');
- for (HWEventListener *Listener : Listeners)
- Listener->onCycleBegin();
-}
-
-void Pipeline::notifyCycleEnd() {
- LLVM_DEBUG(dbgs() << "[E] Cycle end: " << Cycles << "\n\n");
- for (HWEventListener *Listener : Listeners)
- Listener->onCycleEnd();
-}
-} // namespace mca.
diff --git a/tools/llvm-mca/Pipeline.h b/tools/llvm-mca/Pipeline.h
deleted file mode 100644
index 6916e422be39..000000000000
--- a/tools/llvm-mca/Pipeline.h
+++ /dev/null
@@ -1,79 +0,0 @@
-//===--------------------- Pipeline.h ---------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file implements an ordered container of stages that simulate the
-/// pipeline of a hardware backend.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_PIPELINE_H
-#define LLVM_TOOLS_LLVM_MCA_PIPELINE_H
-
-#include "Scheduler.h"
-#include "Stage.h"
-#include "llvm/ADT/SmallVector.h"
-
-namespace mca {
-
-class HWEventListener;
-class HWInstructionEvent;
-class HWStallEvent;
-
-/// A pipeline for a specific subtarget.
-///
-/// It emulates an out-of-order execution of instructions. Instructions are
-/// fetched from a MCInst sequence managed by an initial 'Fetch' stage.
-/// Instructions are firstly fetched, then dispatched to the schedulers, and
-/// then executed.
-///
-/// This class tracks the lifetime of an instruction from the moment where
-/// it gets dispatched to the schedulers, to the moment where it finishes
-/// executing and register writes are architecturally committed.
-/// In particular, it monitors changes in the state of every instruction
-/// in flight.
-///
-/// Instructions are executed in a loop of iterations. The number of iterations
-/// is defined by the SourceMgr object, which is managed by the initial stage
-/// of the instruction pipeline.
-///
-/// The Pipeline entry point is method 'run()' which executes cycles in a loop
-/// until there are new instructions to dispatch, and not every instruction
-/// has been retired.
-///
-/// Internally, the Pipeline collects statistical information in the form of
-/// histograms. For example, it tracks how the dispatch group size changes
-/// over time.
-class Pipeline {
- Pipeline(const Pipeline &P) = delete;
- Pipeline &operator=(const Pipeline &P) = delete;
-
- /// An ordered list of stages that define this instruction pipeline.
- llvm::SmallVector<std::unique_ptr<Stage>, 8> Stages;
- std::set<HWEventListener *> Listeners;
- unsigned Cycles;
-
- void preExecuteStages();
- bool executeStages(InstRef &IR);
- void postExecuteStages();
- void runCycle();
-
- bool hasWorkToProcess();
- void notifyCycleBegin();
- void notifyCycleEnd();
-
-public:
- Pipeline() : Cycles(0) {}
- void appendStage(std::unique_ptr<Stage> S) { Stages.push_back(std::move(S)); }
- void run();
- void addEventListener(HWEventListener *Listener);
-};
-} // namespace mca
-
-#endif // LLVM_TOOLS_LLVM_MCA_PIPELINE_H
diff --git a/tools/llvm-mca/PipelinePrinter.cpp b/tools/llvm-mca/PipelinePrinter.cpp
index c5b1a12b792f..18ef45fc2a65 100644
--- a/tools/llvm-mca/PipelinePrinter.cpp
+++ b/tools/llvm-mca/PipelinePrinter.cpp
@@ -13,14 +13,14 @@
//===----------------------------------------------------------------------===//
#include "PipelinePrinter.h"
-#include "View.h"
+#include "Views/View.h"
+namespace llvm {
namespace mca {
-using namespace llvm;
-
void PipelinePrinter::printReport(llvm::raw_ostream &OS) const {
for (const auto &V : Views)
V->printView(OS);
}
} // namespace mca.
+} // namespace llvm
diff --git a/tools/llvm-mca/PipelinePrinter.h b/tools/llvm-mca/PipelinePrinter.h
index fe871414418f..456026e12df3 100644
--- a/tools/llvm-mca/PipelinePrinter.h
+++ b/tools/llvm-mca/PipelinePrinter.h
@@ -17,13 +17,14 @@
#ifndef LLVM_TOOLS_LLVM_MCA_PIPELINEPRINTER_H
#define LLVM_TOOLS_LLVM_MCA_PIPELINEPRINTER_H
-#include "Pipeline.h"
-#include "View.h"
+#include "Views/View.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/MCA/Pipeline.h"
#include "llvm/Support/raw_ostream.h"
#define DEBUG_TYPE "llvm-mca"
+namespace llvm {
namespace mca {
/// A printer class that knows how to collects statistics on the
@@ -48,5 +49,6 @@ public:
void printReport(llvm::raw_ostream &OS) const;
};
} // namespace mca
+} // namespace llvm
#endif // LLVM_TOOLS_LLVM_MCA_PIPELINEPRINTER_H
diff --git a/tools/llvm-mca/RegisterFile.cpp b/tools/llvm-mca/RegisterFile.cpp
deleted file mode 100644
index 44de105b8996..000000000000
--- a/tools/llvm-mca/RegisterFile.cpp
+++ /dev/null
@@ -1,343 +0,0 @@
-//===--------------------- RegisterFile.cpp ---------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines a register mapping file class. This class is responsible
-/// for managing hardware register files and the tracking of data dependencies
-/// between registers.
-///
-//===----------------------------------------------------------------------===//
-
-#include "RegisterFile.h"
-#include "Instruction.h"
-#include "llvm/Support/Debug.h"
-
-using namespace llvm;
-
-#define DEBUG_TYPE "llvm-mca"
-
-namespace mca {
-
-RegisterFile::RegisterFile(const llvm::MCSchedModel &SM,
- const llvm::MCRegisterInfo &mri, unsigned NumRegs)
- : MRI(mri), RegisterMappings(mri.getNumRegs(),
- {WriteRef(), {IndexPlusCostPairTy(0, 1), 0}}) {
- initialize(SM, NumRegs);
-}
-
-void RegisterFile::initialize(const MCSchedModel &SM, unsigned NumRegs) {
- // Create a default register file that "sees" all the machine registers
- // declared by the target. The number of physical registers in the default
- // register file is set equal to `NumRegs`. A value of zero for `NumRegs`
- // means: this register file has an unbounded number of physical registers.
- addRegisterFile({} /* all registers */, NumRegs);
- if (!SM.hasExtraProcessorInfo())
- return;
-
- // For each user defined register file, allocate a RegisterMappingTracker
- // object. The size of every register file, as well as the mapping between
- // register files and register classes is specified via tablegen.
- const MCExtraProcessorInfo &Info = SM.getExtraProcessorInfo();
- for (unsigned I = 0, E = Info.NumRegisterFiles; I < E; ++I) {
- const MCRegisterFileDesc &RF = Info.RegisterFiles[I];
- // Skip invalid register files with zero physical registers.
- unsigned Length = RF.NumRegisterCostEntries;
- if (!RF.NumPhysRegs)
- continue;
- // The cost of a register definition is equivalent to the number of
- // physical registers that are allocated at register renaming stage.
- const MCRegisterCostEntry *FirstElt =
- &Info.RegisterCostTable[RF.RegisterCostEntryIdx];
- addRegisterFile(ArrayRef<MCRegisterCostEntry>(FirstElt, Length),
- RF.NumPhysRegs);
- }
-}
-
-void RegisterFile::addRegisterFile(ArrayRef<MCRegisterCostEntry> Entries,
- unsigned NumPhysRegs) {
- // A default register file is always allocated at index #0. That register file
- // is mainly used to count the total number of mappings created by all
- // register files at runtime. Users can limit the number of available physical
- // registers in register file #0 through the command line flag
- // `-register-file-size`.
- unsigned RegisterFileIndex = RegisterFiles.size();
- RegisterFiles.emplace_back(NumPhysRegs);
-
- // Special case where there is no register class identifier in the set.
- // An empty set of register classes means: this register file contains all
- // the physical registers specified by the target.
- // We optimistically assume that a register can be renamed at the cost of a
- // single physical register. The constructor of RegisterFile ensures that
- // a RegisterMapping exists for each logical register defined by the Target.
- if (Entries.empty())
- return;
-
- // Now update the cost of individual registers.
- for (const MCRegisterCostEntry &RCE : Entries) {
- const MCRegisterClass &RC = MRI.getRegClass(RCE.RegisterClassID);
- for (const MCPhysReg Reg : RC) {
- RegisterRenamingInfo &Entry = RegisterMappings[Reg].second;
- IndexPlusCostPairTy &IPC = Entry.IndexPlusCost;
- if (IPC.first && IPC.first != RegisterFileIndex) {
- // The only register file that is allowed to overlap is the default
- // register file at index #0. The analysis is inaccurate if register
- // files overlap.
- errs() << "warning: register " << MRI.getName(Reg)
- << " defined in multiple register files.";
- }
- IPC = std::make_pair(RegisterFileIndex, RCE.Cost);
- Entry.RenameAs = Reg;
-
- // Assume the same cost for each sub-register.
- for (MCSubRegIterator I(Reg, &MRI); I.isValid(); ++I) {
- RegisterRenamingInfo &OtherEntry = RegisterMappings[*I].second;
- if (!OtherEntry.IndexPlusCost.first &&
- (!OtherEntry.RenameAs ||
- MRI.isSuperRegister(*I, OtherEntry.RenameAs))) {
- OtherEntry.IndexPlusCost = IPC;
- OtherEntry.RenameAs = Reg;
- }
- }
- }
- }
-}
-
-void RegisterFile::allocatePhysRegs(const RegisterRenamingInfo &Entry,
- MutableArrayRef<unsigned> UsedPhysRegs) {
- unsigned RegisterFileIndex = Entry.IndexPlusCost.first;
- unsigned Cost = Entry.IndexPlusCost.second;
- if (RegisterFileIndex) {
- RegisterMappingTracker &RMT = RegisterFiles[RegisterFileIndex];
- RMT.NumUsedPhysRegs += Cost;
- UsedPhysRegs[RegisterFileIndex] += Cost;
- }
-
- // Now update the default register mapping tracker.
- RegisterFiles[0].NumUsedPhysRegs += Cost;
- UsedPhysRegs[0] += Cost;
-}
-
-void RegisterFile::freePhysRegs(const RegisterRenamingInfo &Entry,
- MutableArrayRef<unsigned> FreedPhysRegs) {
- unsigned RegisterFileIndex = Entry.IndexPlusCost.first;
- unsigned Cost = Entry.IndexPlusCost.second;
- if (RegisterFileIndex) {
- RegisterMappingTracker &RMT = RegisterFiles[RegisterFileIndex];
- RMT.NumUsedPhysRegs -= Cost;
- FreedPhysRegs[RegisterFileIndex] += Cost;
- }
-
- // Now update the default register mapping tracker.
- RegisterFiles[0].NumUsedPhysRegs -= Cost;
- FreedPhysRegs[0] += Cost;
-}
-
-void RegisterFile::addRegisterWrite(WriteRef Write,
- MutableArrayRef<unsigned> UsedPhysRegs,
- bool ShouldAllocatePhysRegs) {
- WriteState &WS = *Write.getWriteState();
- unsigned RegID = WS.getRegisterID();
- assert(RegID && "Adding an invalid register definition?");
-
- LLVM_DEBUG({
- dbgs() << "RegisterFile: addRegisterWrite [ " << Write.getSourceIndex()
- << ", " << MRI.getName(RegID) << "]\n";
- });
-
- // If RenameAs is equal to RegID, then RegID is subject to register renaming
- // and false dependencies on RegID are all eliminated.
-
- // If RenameAs references the invalid register, then we optimistically assume
- // that it can be renamed. In the absence of tablegen descriptors for register
- // files, RenameAs is always set to the invalid register ID. In all other
- // cases, RenameAs must be either equal to RegID, or it must reference a
- // super-register of RegID.
-
- // If RenameAs is a super-register of RegID, then a write to RegID has always
- // a false dependency on RenameAs. The only exception is for when the write
- // implicitly clears the upper portion of the underlying register.
- // If a write clears its super-registers, then it is renamed as `RenameAs`.
- const RegisterRenamingInfo &RRI = RegisterMappings[RegID].second;
- if (RRI.RenameAs && RRI.RenameAs != RegID) {
- RegID = RRI.RenameAs;
- const WriteRef &OtherWrite = RegisterMappings[RegID].first;
-
- if (!WS.clearsSuperRegisters()) {
- // The processor keeps the definition of `RegID` together with register
- // `RenameAs`. Since this partial write is not renamed, no physical
- // register is allocated.
- ShouldAllocatePhysRegs = false;
-
- if (OtherWrite.getSourceIndex() != Write.getSourceIndex()) {
- // This partial write has a false dependency on RenameAs.
- WS.setDependentWrite(OtherWrite.getWriteState());
- }
- }
- }
-
- // Update the mapping for register RegID including its sub-registers.
- RegisterMappings[RegID].first = Write;
- for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I)
- RegisterMappings[*I].first = Write;
-
- // No physical registers are allocated for instructions that are optimized in
- // hardware. For example, zero-latency data-dependency breaking instructions
- // don't consume physical registers.
- if (ShouldAllocatePhysRegs)
- allocatePhysRegs(RegisterMappings[RegID].second, UsedPhysRegs);
-
- if (!WS.clearsSuperRegisters())
- return;
-
- for (MCSuperRegIterator I(RegID, &MRI); I.isValid(); ++I)
- RegisterMappings[*I].first = Write;
-}
-
-void RegisterFile::removeRegisterWrite(const WriteState &WS,
- MutableArrayRef<unsigned> FreedPhysRegs,
- bool ShouldFreePhysRegs) {
- unsigned RegID = WS.getRegisterID();
-
- assert(RegID != 0 && "Invalidating an already invalid register?");
- assert(WS.getCyclesLeft() != UNKNOWN_CYCLES &&
- "Invalidating a write of unknown cycles!");
- assert(WS.getCyclesLeft() <= 0 && "Invalid cycles left for this write!");
-
- unsigned RenameAs = RegisterMappings[RegID].second.RenameAs;
- if (RenameAs && RenameAs != RegID) {
- RegID = RenameAs;
-
- if (!WS.clearsSuperRegisters()) {
- // Keep the definition of `RegID` together with register `RenameAs`.
- ShouldFreePhysRegs = false;
- }
- }
-
- if (ShouldFreePhysRegs)
- freePhysRegs(RegisterMappings[RegID].second, FreedPhysRegs);
-
- WriteRef &WR = RegisterMappings[RegID].first;
- if (WR.getWriteState() == &WS)
- WR.invalidate();
-
- for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I) {
- WriteRef &OtherWR = RegisterMappings[*I].first;
- if (OtherWR.getWriteState() == &WS)
- OtherWR.invalidate();
- }
-
- if (!WS.clearsSuperRegisters())
- return;
-
- for (MCSuperRegIterator I(RegID, &MRI); I.isValid(); ++I) {
- WriteRef &OtherWR = RegisterMappings[*I].first;
- if (OtherWR.getWriteState() == &WS)
- OtherWR.invalidate();
- }
-}
-
-void RegisterFile::collectWrites(SmallVectorImpl<WriteRef> &Writes,
- unsigned RegID) const {
- assert(RegID && RegID < RegisterMappings.size());
- LLVM_DEBUG(dbgs() << "RegisterFile: collecting writes for register "
- << MRI.getName(RegID) << '\n');
- const WriteRef &WR = RegisterMappings[RegID].first;
- if (WR.isValid())
- Writes.push_back(WR);
-
- // Handle potential partial register updates.
- for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I) {
- const WriteRef &WR = RegisterMappings[*I].first;
- if (WR.isValid())
- Writes.push_back(WR);
- }
-
- // Remove duplicate entries and resize the input vector.
- llvm::sort(Writes.begin(), Writes.end(),
- [](const WriteRef &Lhs, const WriteRef &Rhs) {
- return Lhs.getWriteState() < Rhs.getWriteState();
- });
- auto It = std::unique(Writes.begin(), Writes.end());
- Writes.resize(std::distance(Writes.begin(), It));
-
- LLVM_DEBUG({
- for (const WriteRef &WR : Writes) {
- const WriteState &WS = *WR.getWriteState();
- dbgs() << "[PRF] Found a dependent use of Register "
- << MRI.getName(WS.getRegisterID()) << " (defined by intruction #"
- << WR.getSourceIndex() << ")\n";
- }
- });
-}
-
-unsigned RegisterFile::isAvailable(ArrayRef<unsigned> Regs) const {
- SmallVector<unsigned, 4> NumPhysRegs(getNumRegisterFiles());
-
- // Find how many new mappings must be created for each register file.
- for (const unsigned RegID : Regs) {
- const RegisterRenamingInfo &RRI = RegisterMappings[RegID].second;
- const IndexPlusCostPairTy &Entry = RRI.IndexPlusCost;
- if (Entry.first)
- NumPhysRegs[Entry.first] += Entry.second;
- NumPhysRegs[0] += Entry.second;
- }
-
- unsigned Response = 0;
- for (unsigned I = 0, E = getNumRegisterFiles(); I < E; ++I) {
- unsigned NumRegs = NumPhysRegs[I];
- if (!NumRegs)
- continue;
-
- const RegisterMappingTracker &RMT = RegisterFiles[I];
- if (!RMT.NumPhysRegs) {
- // The register file has an unbounded number of microarchitectural
- // registers.
- continue;
- }
-
- if (RMT.NumPhysRegs < NumRegs) {
- // The current register file is too small. This may occur if the number of
- // microarchitectural registers in register file #0 was changed by the
- // users via flag -reg-file-size. Alternatively, the scheduling model
- // specified a too small number of registers for this register file.
- report_fatal_error(
- "Not enough microarchitectural registers in the register file");
- }
-
- if (RMT.NumPhysRegs < (RMT.NumUsedPhysRegs + NumRegs))
- Response |= (1U << I);
- }
-
- return Response;
-}
-
-#ifndef NDEBUG
-void RegisterFile::dump() const {
- for (unsigned I = 0, E = MRI.getNumRegs(); I < E; ++I) {
- const RegisterMapping &RM = RegisterMappings[I];
- if (!RM.first.getWriteState())
- continue;
- const RegisterRenamingInfo &RRI = RM.second;
- dbgs() << MRI.getName(I) << ", " << I << ", PRF=" << RRI.IndexPlusCost.first
- << ", Cost=" << RRI.IndexPlusCost.second
- << ", RenameAs=" << RRI.RenameAs << ", ";
- RM.first.dump();
- dbgs() << '\n';
- }
-
- for (unsigned I = 0, E = getNumRegisterFiles(); I < E; ++I) {
- dbgs() << "Register File #" << I;
- const RegisterMappingTracker &RMT = RegisterFiles[I];
- dbgs() << "\n TotalMappings: " << RMT.NumPhysRegs
- << "\n NumUsedMappings: " << RMT.NumUsedPhysRegs << '\n';
- }
-}
-#endif
-
-} // namespace mca
diff --git a/tools/llvm-mca/RegisterFile.h b/tools/llvm-mca/RegisterFile.h
deleted file mode 100644
index 349e9789b6ee..000000000000
--- a/tools/llvm-mca/RegisterFile.h
+++ /dev/null
@@ -1,172 +0,0 @@
-//===--------------------- RegisterFile.h -----------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines a register mapping file class. This class is responsible
-/// for managing hardware register files and the tracking of data dependencies
-/// between registers.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_REGISTER_FILE_H
-#define LLVM_TOOLS_LLVM_MCA_REGISTER_FILE_H
-
-#include "HardwareUnit.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/MC/MCRegisterInfo.h"
-#include "llvm/MC/MCSchedule.h"
-
-namespace mca {
-
-class ReadState;
-class WriteState;
-class WriteRef;
-
-/// Manages hardware register files, and tracks register definitions for
-/// register renaming purposes.
-class RegisterFile : public HardwareUnit {
- const llvm::MCRegisterInfo &MRI;
-
- // Each register file is associated with an instance of
- // RegisterMappingTracker.
- // A RegisterMappingTracker keeps track of the number of physical registers
- // which have been dynamically allocated by the simulator.
- struct RegisterMappingTracker {
- // The total number of physical registers that are available in this
- // register file for register renaming purpouses. A value of zero for this
- // field means: this register file has an unbounded number of physical
- // registers.
- const unsigned NumPhysRegs;
- // Number of physical registers that are currently in use.
- unsigned NumUsedPhysRegs;
-
- RegisterMappingTracker(unsigned NumPhysRegisters)
- : NumPhysRegs(NumPhysRegisters), NumUsedPhysRegs(0) {}
- };
-
- // A vector of register file descriptors. This set always contains at least
- // one entry. Entry at index #0 is reserved. That entry describes a register
- // file with an unbounded number of physical registers that "sees" all the
- // hardware registers declared by the target (i.e. all the register
- // definitions in the target specific `XYZRegisterInfo.td` - where `XYZ` is
- // the target name).
- //
- // Users can limit the number of physical registers that are available in
- // regsiter file #0 specifying command line flag `-register-file-size=<uint>`.
- llvm::SmallVector<RegisterMappingTracker, 4> RegisterFiles;
-
- // This type is used to propagate information about the owner of a register,
- // and the cost of allocating it in the PRF. Register cost is defined as the
- // number of physical registers consumed by the PRF to allocate a user
- // register.
- //
- // For example: on X86 BtVer2, a YMM register consumes 2 128-bit physical
- // registers. So, the cost of allocating a YMM register in BtVer2 is 2.
- using IndexPlusCostPairTy = std::pair<unsigned, unsigned>;
-
- // Struct RegisterRenamingInfo maps registers to register files.
- // There is a RegisterRenamingInfo object for every register defined by
- // the target. RegisteRenamingInfo objects are stored into vector
- // RegisterMappings, and register IDs can be used to reference them.
- struct RegisterRenamingInfo {
- IndexPlusCostPairTy IndexPlusCost;
- llvm::MCPhysReg RenameAs;
- };
-
- // RegisterMapping objects are mainly used to track physical register
- // definitions. There is a RegisterMapping for every register defined by the
- // Target. For each register, a RegisterMapping pair contains a descriptor of
- // the last register write (in the form of a WriteRef object), as well as a
- // RegisterRenamingInfo to quickly identify owning register files.
- //
- // This implementation does not allow overlapping register files. The only
- // register file that is allowed to overlap with other register files is
- // register file #0. If we exclude register #0, every register is "owned" by
- // at most one register file.
- using RegisterMapping = std::pair<WriteRef, RegisterRenamingInfo>;
-
- // This map contains one entry for each register defined by the target.
- std::vector<RegisterMapping> RegisterMappings;
-
- // This method creates a new register file descriptor.
- // The new register file owns all of the registers declared by register
- // classes in the 'RegisterClasses' set.
- //
- // Processor models allow the definition of RegisterFile(s) via tablegen. For
- // example, this is a tablegen definition for a x86 register file for
- // XMM[0-15] and YMM[0-15], that allows up to 60 renames (each rename costs 1
- // physical register).
- //
- // def FPRegisterFile : RegisterFile<60, [VR128RegClass, VR256RegClass]>
- //
- // Here FPRegisterFile contains all the registers defined by register class
- // VR128RegClass and VR256RegClass. FPRegisterFile implements 60
- // registers which can be used for register renaming purpose.
- void
- addRegisterFile(llvm::ArrayRef<llvm::MCRegisterCostEntry> RegisterClasses,
- unsigned NumPhysRegs);
-
- // Consumes physical registers in each register file specified by the
- // `IndexPlusCostPairTy`. This method is called from `addRegisterMapping()`.
- void allocatePhysRegs(const RegisterRenamingInfo &Entry,
- llvm::MutableArrayRef<unsigned> UsedPhysRegs);
-
- // Releases previously allocated physical registers from the register file(s).
- // This method is called from `invalidateRegisterMapping()`.
- void freePhysRegs(const RegisterRenamingInfo &Entry,
- llvm::MutableArrayRef<unsigned> FreedPhysRegs);
-
- // Create an instance of RegisterMappingTracker for every register file
- // specified by the processor model.
- // If no register file is specified, then this method creates a default
- // register file with an unbounded number of physical registers.
- void initialize(const llvm::MCSchedModel &SM, unsigned NumRegs);
-
-public:
- RegisterFile(const llvm::MCSchedModel &SM, const llvm::MCRegisterInfo &mri,
- unsigned NumRegs = 0);
-
- // This method updates the register mappings inserting a new register
- // definition. This method is also responsible for updating the number of
- // allocated physical registers in each register file modified by the write.
- // No physical regiser is allocated when flag ShouldAllocatePhysRegs is set.
- void addRegisterWrite(WriteRef Write,
- llvm::MutableArrayRef<unsigned> UsedPhysRegs,
- bool ShouldAllocatePhysRegs = true);
-
- // Removes write \param WS from the register mappings.
- // Physical registers may be released to reflect this update.
- void removeRegisterWrite(const WriteState &WS,
- llvm::MutableArrayRef<unsigned> FreedPhysRegs,
- bool ShouldFreePhysRegs = true);
-
- // Checks if there are enough physical registers in the register files.
- // Returns a "response mask" where each bit represents the response from a
- // different register file. A mask of all zeroes means that all register
- // files are available. Otherwise, the mask can be used to identify which
- // register file was busy. This sematic allows us to classify dispatch
- // stalls caused by the lack of register file resources.
- //
- // Current implementation can simulate up to 32 register files (including the
- // special register file at index #0).
- unsigned isAvailable(llvm::ArrayRef<unsigned> Regs) const;
- void collectWrites(llvm::SmallVectorImpl<WriteRef> &Writes,
- unsigned RegID) const;
- void updateOnRead(ReadState &RS, unsigned RegID);
-
- unsigned getNumRegisterFiles() const { return RegisterFiles.size(); }
-
-#ifndef NDEBUG
- void dump() const;
-#endif
-};
-
-} // namespace mca
-
-#endif // LLVM_TOOLS_LLVM_MCA_REGISTER_FILE_H
diff --git a/tools/llvm-mca/RegisterFileStatistics.cpp b/tools/llvm-mca/RegisterFileStatistics.cpp
deleted file mode 100644
index 1b07bf9a3b33..000000000000
--- a/tools/llvm-mca/RegisterFileStatistics.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-//===--------------------- RegisterFileStatistics.cpp -----------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file implements the RegisterFileStatistics interface.
-///
-//===----------------------------------------------------------------------===//
-
-#include "RegisterFileStatistics.h"
-#include "llvm/Support/Format.h"
-
-using namespace llvm;
-
-namespace mca {
-
-void RegisterFileStatistics::initializeRegisterFileInfo() {
- const MCSchedModel &SM = STI.getSchedModel();
- RegisterFileUsage Empty = {0, 0, 0};
- if (!SM.hasExtraProcessorInfo()) {
- // Assume a single register file.
- RegisterFiles.emplace_back(Empty);
- return;
- }
-
- // Initialize a RegisterFileUsage for every user defined register file, plus
- // the default register file which is always at index #0.
- const MCExtraProcessorInfo &PI = SM.getExtraProcessorInfo();
- // There is always an "InvalidRegisterFile" entry in tablegen. That entry can
- // be skipped. If there are no user defined register files, then reserve a
- // single entry for the default register file at index #0.
- unsigned NumRegFiles = std::max(PI.NumRegisterFiles, 1U);
- RegisterFiles.resize(NumRegFiles);
- std::fill(RegisterFiles.begin(), RegisterFiles.end(), Empty);
-}
-
-void RegisterFileStatistics::onEvent(const HWInstructionEvent &Event) {
- switch (Event.Type) {
- default:
- break;
- case HWInstructionEvent::Retired: {
- const auto &RE = static_cast<const HWInstructionRetiredEvent &>(Event);
- for (unsigned I = 0, E = RegisterFiles.size(); I < E; ++I)
- RegisterFiles[I].CurrentlyUsedMappings -= RE.FreedPhysRegs[I];
- break;
- }
- case HWInstructionEvent::Dispatched: {
- const auto &DE = static_cast<const HWInstructionDispatchedEvent &>(Event);
- for (unsigned I = 0, E = RegisterFiles.size(); I < E; ++I) {
- RegisterFileUsage &RFU = RegisterFiles[I];
- unsigned NumUsedPhysRegs = DE.UsedPhysRegs[I];
- RFU.CurrentlyUsedMappings += NumUsedPhysRegs;
- RFU.TotalMappings += NumUsedPhysRegs;
- RFU.MaxUsedMappings =
- std::max(RFU.MaxUsedMappings, RFU.CurrentlyUsedMappings);
- }
- }
- }
-}
-
-void RegisterFileStatistics::printView(raw_ostream &OS) const {
- std::string Buffer;
- raw_string_ostream TempStream(Buffer);
-
- TempStream << "\n\nRegister File statistics:";
- const RegisterFileUsage &GlobalUsage = RegisterFiles[0];
- TempStream << "\nTotal number of mappings created: "
- << GlobalUsage.TotalMappings;
- TempStream << "\nMax number of mappings used: "
- << GlobalUsage.MaxUsedMappings << '\n';
-
- for (unsigned I = 1, E = RegisterFiles.size(); I < E; ++I) {
- const RegisterFileUsage &RFU = RegisterFiles[I];
- // Obtain the register file descriptor from the scheduling model.
- assert(STI.getSchedModel().hasExtraProcessorInfo() &&
- "Unable to find register file info!");
- const MCExtraProcessorInfo &PI =
- STI.getSchedModel().getExtraProcessorInfo();
- assert(I <= PI.NumRegisterFiles && "Unexpected register file index!");
- const MCRegisterFileDesc &RFDesc = PI.RegisterFiles[I];
- // Skip invalid register files.
- if (!RFDesc.NumPhysRegs)
- continue;
-
- TempStream << "\n* Register File #" << I;
- TempStream << " -- " << StringRef(RFDesc.Name) << ':';
- TempStream << "\n Number of physical registers: ";
- if (!RFDesc.NumPhysRegs)
- TempStream << "unbounded";
- else
- TempStream << RFDesc.NumPhysRegs;
- TempStream << "\n Total number of mappings created: "
- << RFU.TotalMappings;
- TempStream << "\n Max number of mappings used: "
- << RFU.MaxUsedMappings << '\n';
- }
-
- TempStream.flush();
- OS << Buffer;
-}
-
-} // namespace mca
diff --git a/tools/llvm-mca/RetireControlUnit.cpp b/tools/llvm-mca/RetireControlUnit.cpp
deleted file mode 100644
index 123058541f28..000000000000
--- a/tools/llvm-mca/RetireControlUnit.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-//===---------------------- RetireControlUnit.cpp ---------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file simulates the hardware responsible for retiring instructions.
-///
-//===----------------------------------------------------------------------===//
-
-#include "RetireControlUnit.h"
-#include "llvm/Support/Debug.h"
-
-using namespace llvm;
-
-#define DEBUG_TYPE "llvm-mca"
-
-namespace mca {
-
-RetireControlUnit::RetireControlUnit(const llvm::MCSchedModel &SM)
- : NextAvailableSlotIdx(0), CurrentInstructionSlotIdx(0),
- AvailableSlots(SM.MicroOpBufferSize), MaxRetirePerCycle(0) {
- // Check if the scheduling model provides extra information about the machine
- // processor. If so, then use that information to set the reorder buffer size
- // and the maximum number of instructions retired per cycle.
- if (SM.hasExtraProcessorInfo()) {
- const MCExtraProcessorInfo &EPI = SM.getExtraProcessorInfo();
- if (EPI.ReorderBufferSize)
- AvailableSlots = EPI.ReorderBufferSize;
- MaxRetirePerCycle = EPI.MaxRetirePerCycle;
- }
-
- assert(AvailableSlots && "Invalid reorder buffer size!");
- Queue.resize(AvailableSlots);
-}
-
-// Reserves a number of slots, and returns a new token.
-unsigned RetireControlUnit::reserveSlot(const InstRef &IR,
- unsigned NumMicroOps) {
- assert(isAvailable(NumMicroOps));
- unsigned NormalizedQuantity =
- std::min(NumMicroOps, static_cast<unsigned>(Queue.size()));
- // Zero latency instructions may have zero mOps. Artificially bump this
- // value to 1. Although zero latency instructions don't consume scheduler
- // resources, they still consume one slot in the retire queue.
- NormalizedQuantity = std::max(NormalizedQuantity, 1U);
- unsigned TokenID = NextAvailableSlotIdx;
- Queue[NextAvailableSlotIdx] = {IR, NormalizedQuantity, false};
- NextAvailableSlotIdx += NormalizedQuantity;
- NextAvailableSlotIdx %= Queue.size();
- AvailableSlots -= NormalizedQuantity;
- return TokenID;
-}
-
-const RetireControlUnit::RUToken &RetireControlUnit::peekCurrentToken() const {
- return Queue[CurrentInstructionSlotIdx];
-}
-
-void RetireControlUnit::consumeCurrentToken() {
- const RetireControlUnit::RUToken &Current = peekCurrentToken();
- assert(Current.NumSlots && "Reserved zero slots?");
- assert(Current.IR.isValid() && "Invalid RUToken in the RCU queue.");
-
- // Update the slot index to be the next item in the circular queue.
- CurrentInstructionSlotIdx += Current.NumSlots;
- CurrentInstructionSlotIdx %= Queue.size();
- AvailableSlots += Current.NumSlots;
-}
-
-void RetireControlUnit::onInstructionExecuted(unsigned TokenID) {
- assert(Queue.size() > TokenID);
- assert(Queue[TokenID].Executed == false && Queue[TokenID].IR.isValid());
- Queue[TokenID].Executed = true;
-}
-
-#ifndef NDEBUG
-void RetireControlUnit::dump() const {
- dbgs() << "Retire Unit: { Total Slots=" << Queue.size()
- << ", Available Slots=" << AvailableSlots << " }\n";
-}
-#endif
-
-} // namespace mca
diff --git a/tools/llvm-mca/RetireControlUnit.h b/tools/llvm-mca/RetireControlUnit.h
deleted file mode 100644
index 8acc8bcc98fe..000000000000
--- a/tools/llvm-mca/RetireControlUnit.h
+++ /dev/null
@@ -1,98 +0,0 @@
-//===---------------------- RetireControlUnit.h -----------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file simulates the hardware responsible for retiring instructions.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_RETIRE_CONTROL_UNIT_H
-#define LLVM_TOOLS_LLVM_MCA_RETIRE_CONTROL_UNIT_H
-
-#include "HardwareUnit.h"
-#include "Instruction.h"
-#include "llvm/MC/MCSchedule.h"
-#include <vector>
-
-namespace mca {
-
-/// This class tracks which instructions are in-flight (i.e., dispatched but not
-/// retired) in the OoO backend.
-//
-/// This class checks on every cycle if/which instructions can be retired.
-/// Instructions are retired in program order.
-/// In the event of an instruction being retired, the pipeline that owns
-/// this RetireControlUnit (RCU) gets notified.
-///
-/// On instruction retired, register updates are all architecturally
-/// committed, and any physicall registers previously allocated for the
-/// retired instruction are freed.
-struct RetireControlUnit : public HardwareUnit {
- // A RUToken is created by the RCU for every instruction dispatched to the
- // schedulers. These "tokens" are managed by the RCU in its token Queue.
- //
- // On every cycle ('cycleEvent'), the RCU iterates through the token queue
- // looking for any token with its 'Executed' flag set. If a token has that
- // flag set, then the instruction has reached the write-back stage and will
- // be retired by the RCU.
- //
- // 'NumSlots' represents the number of entries consumed by the instruction in
- // the reorder buffer. Those entries will become available again once the
- // instruction is retired.
- //
- // Note that the size of the reorder buffer is defined by the scheduling
- // model via field 'NumMicroOpBufferSize'.
- struct RUToken {
- InstRef IR;
- unsigned NumSlots; // Slots reserved to this instruction.
- bool Executed; // True if the instruction is past the WB stage.
- };
-
-private:
- unsigned NextAvailableSlotIdx;
- unsigned CurrentInstructionSlotIdx;
- unsigned AvailableSlots;
- unsigned MaxRetirePerCycle; // 0 means no limit.
- std::vector<RUToken> Queue;
-
-public:
- RetireControlUnit(const llvm::MCSchedModel &SM);
-
- bool isFull() const { return !AvailableSlots; }
- bool isEmpty() const { return AvailableSlots == Queue.size(); }
- bool isAvailable(unsigned Quantity = 1) const {
- // Some instructions may declare a number of uOps which exceeds the size
- // of the reorder buffer. To avoid problems, cap the amount of slots to
- // the size of the reorder buffer.
- Quantity = std::min(Quantity, static_cast<unsigned>(Queue.size()));
- return AvailableSlots >= Quantity;
- }
-
- unsigned getMaxRetirePerCycle() const { return MaxRetirePerCycle; }
-
- // Reserves a number of slots, and returns a new token.
- unsigned reserveSlot(const InstRef &IS, unsigned NumMicroOps);
-
- // Return the current token from the RCU's circular token queue.
- const RUToken &peekCurrentToken() const;
-
- // Advance the pointer to the next token in the circular token queue.
- void consumeCurrentToken();
-
- // Update the RCU token to represent the executed state.
- void onInstructionExecuted(unsigned TokenID);
-
-#ifndef NDEBUG
- void dump() const;
-#endif
-};
-
-} // namespace mca
-
-#endif // LLVM_TOOLS_LLVM_MCA_RETIRE_CONTROL_UNIT_H
diff --git a/tools/llvm-mca/RetireControlUnitStatistics.cpp b/tools/llvm-mca/RetireControlUnitStatistics.cpp
deleted file mode 100644
index edb855e11e84..000000000000
--- a/tools/llvm-mca/RetireControlUnitStatistics.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-//===--------------------- RetireControlUnitStatistics.cpp ------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file implements the RetireControlUnitStatistics interface.
-///
-//===----------------------------------------------------------------------===//
-
-#include "RetireControlUnitStatistics.h"
-#include "llvm/Support/Format.h"
-
-using namespace llvm;
-
-namespace mca {
-
-void RetireControlUnitStatistics::onEvent(const HWInstructionEvent &Event) {
- if (Event.Type == HWInstructionEvent::Retired)
- ++NumRetired;
-}
-
-void RetireControlUnitStatistics::printView(llvm::raw_ostream &OS) const {
- std::string Buffer;
- raw_string_ostream TempStream(Buffer);
- TempStream << "\n\nRetire Control Unit - "
- << "number of cycles where we saw N instructions retired:\n";
- TempStream << "[# retired], [# cycles]\n";
-
- for (const std::pair<unsigned, unsigned> &Entry : RetiredPerCycle) {
- TempStream << " " << Entry.first;
- if (Entry.first < 10)
- TempStream << ", ";
- else
- TempStream << ", ";
- TempStream << Entry.second << " ("
- << format("%.1f", ((double)Entry.second / NumCycles) * 100.0)
- << "%)\n";
- }
-
- TempStream.flush();
- OS << Buffer;
-}
-
-} // namespace mca
diff --git a/tools/llvm-mca/RetireStage.cpp b/tools/llvm-mca/RetireStage.cpp
deleted file mode 100644
index 55c3b887e478..000000000000
--- a/tools/llvm-mca/RetireStage.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-//===---------------------- RetireStage.cpp ---------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines the retire stage of an instruction pipeline.
-/// The RetireStage represents the process logic that interacts with the
-/// simulated RetireControlUnit hardware.
-///
-//===----------------------------------------------------------------------===//
-
-#include "RetireStage.h"
-#include "HWEventListener.h"
-#include "llvm/Support/Debug.h"
-
-using namespace llvm;
-
-#define DEBUG_TYPE "llvm-mca"
-
-namespace mca {
-
-void RetireStage::cycleStart() {
- if (RCU.isEmpty())
- return;
-
- const unsigned MaxRetirePerCycle = RCU.getMaxRetirePerCycle();
- unsigned NumRetired = 0;
- while (!RCU.isEmpty()) {
- if (MaxRetirePerCycle != 0 && NumRetired == MaxRetirePerCycle)
- break;
- const RetireControlUnit::RUToken &Current = RCU.peekCurrentToken();
- if (!Current.Executed)
- break;
- RCU.consumeCurrentToken();
- notifyInstructionRetired(Current.IR);
- NumRetired++;
- }
-}
-
-void RetireStage::notifyInstructionRetired(const InstRef &IR) {
- LLVM_DEBUG(dbgs() << "[E] Instruction Retired: #" << IR << '\n');
- SmallVector<unsigned, 4> FreedRegs(PRF.getNumRegisterFiles());
- const Instruction &Inst = *IR.getInstruction();
- const InstrDesc &Desc = Inst.getDesc();
-
- bool ShouldFreeRegs = !(Desc.isZeroLatency() && Inst.isDependencyBreaking());
- for (const std::unique_ptr<WriteState> &WS : Inst.getDefs())
- PRF.removeRegisterWrite(*WS.get(), FreedRegs, ShouldFreeRegs);
- notifyEvent<HWInstructionEvent>(HWInstructionRetiredEvent(IR, FreedRegs));
-}
-
-} // namespace mca
diff --git a/tools/llvm-mca/RetireStage.h b/tools/llvm-mca/RetireStage.h
deleted file mode 100644
index 8cf672d92c6e..000000000000
--- a/tools/llvm-mca/RetireStage.h
+++ /dev/null
@@ -1,48 +0,0 @@
-//===---------------------- RetireStage.h -----------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines the retire stage of an instruction pipeline.
-/// The RetireStage represents the process logic that interacts with the
-/// simulated RetireControlUnit hardware.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_RETIRE_STAGE_H
-#define LLVM_TOOLS_LLVM_MCA_RETIRE_STAGE_H
-
-#include "RegisterFile.h"
-#include "RetireControlUnit.h"
-#include "Stage.h"
-
-namespace mca {
-
-class RetireStage : public Stage {
- // Owner will go away when we move listeners/eventing to the stages.
- RetireControlUnit &RCU;
- RegisterFile &PRF;
-
-public:
- RetireStage(RetireControlUnit &R, RegisterFile &F)
- : Stage(), RCU(R), PRF(F) {}
- RetireStage(const RetireStage &Other) = delete;
- RetireStage &operator=(const RetireStage &Other) = delete;
-
- virtual bool hasWorkToComplete() const override final {
- return !RCU.isEmpty();
- }
- virtual void cycleStart() override final;
- virtual bool execute(InstRef &IR) override final { return true; }
- void notifyInstructionRetired(const InstRef &IR);
- void onInstructionExecuted(unsigned TokenID);
-};
-
-} // namespace mca
-
-#endif // LLVM_TOOLS_LLVM_MCA_RETIRE_STAGE_H
diff --git a/tools/llvm-mca/Scheduler.cpp b/tools/llvm-mca/Scheduler.cpp
deleted file mode 100644
index 975a50e4b638..000000000000
--- a/tools/llvm-mca/Scheduler.cpp
+++ /dev/null
@@ -1,403 +0,0 @@
-//===--------------------- Scheduler.cpp ------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// A scheduler for processor resource units and processor resource groups.
-//
-//===----------------------------------------------------------------------===//
-
-#include "Scheduler.h"
-#include "Support.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/raw_ostream.h"
-
-namespace mca {
-
-using namespace llvm;
-
-#define DEBUG_TYPE "llvm-mca"
-
-uint64_t ResourceState::selectNextInSequence() {
- assert(isReady());
- uint64_t Next = getNextInSequence();
- while (!isSubResourceReady(Next)) {
- updateNextInSequence();
- Next = getNextInSequence();
- }
- return Next;
-}
-
-#ifndef NDEBUG
-void ResourceState::dump() const {
- dbgs() << "MASK: " << ResourceMask << ", SIZE_MASK: " << ResourceSizeMask
- << ", NEXT: " << NextInSequenceMask << ", RDYMASK: " << ReadyMask
- << ", BufferSize=" << BufferSize
- << ", AvailableSlots=" << AvailableSlots
- << ", Reserved=" << Unavailable << '\n';
-}
-#endif
-
-void ResourceManager::initialize(const llvm::MCSchedModel &SM) {
- computeProcResourceMasks(SM, ProcResID2Mask);
- for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I)
- addResource(*SM.getProcResource(I), I, ProcResID2Mask[I]);
-}
-
-// Adds a new resource state in Resources, as well as a new descriptor in
-// ResourceDescriptor. Map 'Resources' allows to quickly obtain ResourceState
-// objects from resource mask identifiers.
-void ResourceManager::addResource(const MCProcResourceDesc &Desc,
- unsigned Index, uint64_t Mask) {
- assert(Resources.find(Mask) == Resources.end() && "Resource already added!");
- Resources[Mask] = llvm::make_unique<ResourceState>(Desc, Index, Mask);
-}
-
-// Returns the actual resource consumed by this Use.
-// First, is the primary resource ID.
-// Second, is the specific sub-resource ID.
-std::pair<uint64_t, uint64_t> ResourceManager::selectPipe(uint64_t ResourceID) {
- ResourceState &RS = *Resources[ResourceID];
- uint64_t SubResourceID = RS.selectNextInSequence();
- if (RS.isAResourceGroup())
- return selectPipe(SubResourceID);
- return std::pair<uint64_t, uint64_t>(ResourceID, SubResourceID);
-}
-
-void ResourceState::removeFromNextInSequence(uint64_t ID) {
- assert(NextInSequenceMask);
- assert(countPopulation(ID) == 1);
- if (ID > getNextInSequence())
- RemovedFromNextInSequence |= ID;
- NextInSequenceMask = NextInSequenceMask & (~ID);
- if (!NextInSequenceMask) {
- NextInSequenceMask = ResourceSizeMask;
- assert(NextInSequenceMask != RemovedFromNextInSequence);
- NextInSequenceMask ^= RemovedFromNextInSequence;
- RemovedFromNextInSequence = 0;
- }
-}
-
-void ResourceManager::use(ResourceRef RR) {
- // Mark the sub-resource referenced by RR as used.
- ResourceState &RS = *Resources[RR.first];
- RS.markSubResourceAsUsed(RR.second);
- // If there are still available units in RR.first,
- // then we are done.
- if (RS.isReady())
- return;
-
- // Notify to other resources that RR.first is no longer available.
- for (const std::pair<uint64_t, UniqueResourceState> &Res : Resources) {
- ResourceState &Current = *Res.second.get();
- if (!Current.isAResourceGroup() || Current.getResourceMask() == RR.first)
- continue;
-
- if (Current.containsResource(RR.first)) {
- Current.markSubResourceAsUsed(RR.first);
- Current.removeFromNextInSequence(RR.first);
- }
- }
-}
-
-void ResourceManager::release(ResourceRef RR) {
- ResourceState &RS = *Resources[RR.first];
- bool WasFullyUsed = !RS.isReady();
- RS.releaseSubResource(RR.second);
- if (!WasFullyUsed)
- return;
-
- for (const std::pair<uint64_t, UniqueResourceState> &Res : Resources) {
- ResourceState &Current = *Res.second.get();
- if (!Current.isAResourceGroup() || Current.getResourceMask() == RR.first)
- continue;
-
- if (Current.containsResource(RR.first))
- Current.releaseSubResource(RR.first);
- }
-}
-
-ResourceStateEvent
-ResourceManager::canBeDispatched(ArrayRef<uint64_t> Buffers) const {
- ResourceStateEvent Result = ResourceStateEvent::RS_BUFFER_AVAILABLE;
- for (uint64_t Buffer : Buffers) {
- Result = isBufferAvailable(Buffer);
- if (Result != ResourceStateEvent::RS_BUFFER_AVAILABLE)
- break;
- }
- return Result;
-}
-
-void ResourceManager::reserveBuffers(ArrayRef<uint64_t> Buffers) {
- for (const uint64_t R : Buffers) {
- reserveBuffer(R);
- ResourceState &Resource = *Resources[R];
- if (Resource.isADispatchHazard()) {
- assert(!Resource.isReserved());
- Resource.setReserved();
- }
- }
-}
-
-void ResourceManager::releaseBuffers(ArrayRef<uint64_t> Buffers) {
- for (const uint64_t R : Buffers)
- releaseBuffer(R);
-}
-
-bool ResourceManager::canBeIssued(const InstrDesc &Desc) const {
- return std::all_of(Desc.Resources.begin(), Desc.Resources.end(),
- [&](const std::pair<uint64_t, const ResourceUsage> &E) {
- unsigned NumUnits =
- E.second.isReserved() ? 0U : E.second.NumUnits;
- return isReady(E.first, NumUnits);
- });
-}
-
-// Returns true if all resources are in-order, and there is at least one
-// resource which is a dispatch hazard (BufferSize = 0).
-bool ResourceManager::mustIssueImmediately(const InstrDesc &Desc) {
- if (!canBeIssued(Desc))
- return false;
- bool AllInOrderResources = all_of(Desc.Buffers, [&](uint64_t BufferMask) {
- const ResourceState &Resource = *Resources[BufferMask];
- return Resource.isInOrder() || Resource.isADispatchHazard();
- });
- if (!AllInOrderResources)
- return false;
-
- return any_of(Desc.Buffers, [&](uint64_t BufferMask) {
- return Resources[BufferMask]->isADispatchHazard();
- });
-}
-
-void ResourceManager::issueInstruction(
- const InstrDesc &Desc,
- SmallVectorImpl<std::pair<ResourceRef, double>> &Pipes) {
- for (const std::pair<uint64_t, ResourceUsage> &R : Desc.Resources) {
- const CycleSegment &CS = R.second.CS;
- if (!CS.size()) {
- releaseResource(R.first);
- continue;
- }
-
- assert(CS.begin() == 0 && "Invalid {Start, End} cycles!");
- if (!R.second.isReserved()) {
- ResourceRef Pipe = selectPipe(R.first);
- use(Pipe);
- BusyResources[Pipe] += CS.size();
- // Replace the resource mask with a valid processor resource index.
- const ResourceState &RS = *Resources[Pipe.first];
- Pipe.first = RS.getProcResourceID();
- Pipes.emplace_back(
- std::pair<ResourceRef, double>(Pipe, static_cast<double>(CS.size())));
- } else {
- assert((countPopulation(R.first) > 1) && "Expected a group!");
- // Mark this group as reserved.
- assert(R.second.isReserved());
- reserveResource(R.first);
- BusyResources[ResourceRef(R.first, R.first)] += CS.size();
- }
- }
-}
-
-void ResourceManager::cycleEvent(SmallVectorImpl<ResourceRef> &ResourcesFreed) {
- for (std::pair<ResourceRef, unsigned> &BR : BusyResources) {
- if (BR.second)
- BR.second--;
- if (!BR.second) {
- // Release this resource.
- const ResourceRef &RR = BR.first;
-
- if (countPopulation(RR.first) == 1)
- release(RR);
-
- releaseResource(RR.first);
- ResourcesFreed.push_back(RR);
- }
- }
-
- for (const ResourceRef &RF : ResourcesFreed)
- BusyResources.erase(RF);
-}
-
-#ifndef NDEBUG
-void Scheduler::dump() const {
- dbgs() << "[SCHEDULER]: WaitQueue size is: " << WaitQueue.size() << '\n';
- dbgs() << "[SCHEDULER]: ReadyQueue size is: " << ReadyQueue.size() << '\n';
- dbgs() << "[SCHEDULER]: IssuedQueue size is: " << IssuedQueue.size() << '\n';
- Resources->dump();
-}
-#endif
-
-bool Scheduler::canBeDispatched(const InstRef &IR,
- HWStallEvent::GenericEventType &Event) const {
- Event = HWStallEvent::Invalid;
- const InstrDesc &Desc = IR.getInstruction()->getDesc();
-
- if (Desc.MayLoad && LSU->isLQFull())
- Event = HWStallEvent::LoadQueueFull;
- else if (Desc.MayStore && LSU->isSQFull())
- Event = HWStallEvent::StoreQueueFull;
- else {
- switch (Resources->canBeDispatched(Desc.Buffers)) {
- default:
- return true;
- case ResourceStateEvent::RS_BUFFER_UNAVAILABLE:
- Event = HWStallEvent::SchedulerQueueFull;
- break;
- case ResourceStateEvent::RS_RESERVED:
- Event = HWStallEvent::DispatchGroupStall;
- }
- }
-
- return false;
-}
-
-void Scheduler::issueInstructionImpl(
- InstRef &IR,
- SmallVectorImpl<std::pair<ResourceRef, double>> &UsedResources) {
- Instruction *IS = IR.getInstruction();
- const InstrDesc &D = IS->getDesc();
-
- // Issue the instruction and collect all the consumed resources
- // into a vector. That vector is then used to notify the listener.
- Resources->issueInstruction(D, UsedResources);
-
- // Notify the instruction that it started executing.
- // This updates the internal state of each write.
- IS->execute();
-
- if (IS->isExecuting())
- IssuedQueue[IR.getSourceIndex()] = IS;
-}
-
-// Release the buffered resources and issue the instruction.
-void Scheduler::issueInstruction(
- InstRef &IR,
- SmallVectorImpl<std::pair<ResourceRef, double>> &UsedResources) {
- const InstrDesc &Desc = IR.getInstruction()->getDesc();
- releaseBuffers(Desc.Buffers);
- issueInstructionImpl(IR, UsedResources);
-}
-
-void Scheduler::promoteToReadyQueue(SmallVectorImpl<InstRef> &Ready) {
- // Scan the set of waiting instructions and promote them to the
- // ready queue if operands are all ready.
- for (auto I = WaitQueue.begin(), E = WaitQueue.end(); I != E;) {
- const unsigned IID = I->first;
- Instruction *IS = I->second;
-
- // Check if this instruction is now ready. In case, force
- // a transition in state using method 'update()'.
- if (!IS->isReady())
- IS->update();
-
- const InstrDesc &Desc = IS->getDesc();
- bool IsMemOp = Desc.MayLoad || Desc.MayStore;
- if (!IS->isReady() || (IsMemOp && !LSU->isReady({IID, IS}))) {
- ++I;
- continue;
- }
-
- Ready.emplace_back(IID, IS);
- ReadyQueue[IID] = IS;
- auto ToRemove = I;
- ++I;
- WaitQueue.erase(ToRemove);
- }
-}
-
-InstRef Scheduler::select() {
- // Find the oldest ready-to-issue instruction in the ReadyQueue.
- auto It = std::find_if(ReadyQueue.begin(), ReadyQueue.end(),
- [&](const QueueEntryTy &Entry) {
- const InstrDesc &D = Entry.second->getDesc();
- return Resources->canBeIssued(D);
- });
-
- if (It == ReadyQueue.end())
- return {0, nullptr};
-
- // We want to prioritize older instructions over younger instructions to
- // minimize the pressure on the reorder buffer. We also want to
- // rank higher the instructions with more users to better expose ILP.
-
- // Compute a rank value based on the age of an instruction (i.e. its source
- // index) and its number of users. The lower the rank value, the better.
- int Rank = It->first - It->second->getNumUsers();
- for (auto I = It, E = ReadyQueue.end(); I != E; ++I) {
- int CurrentRank = I->first - I->second->getNumUsers();
- if (CurrentRank < Rank) {
- const InstrDesc &D = I->second->getDesc();
- if (Resources->canBeIssued(D))
- It = I;
- }
- }
-
- // We found an instruction to issue.
- InstRef IR(It->first, It->second);
- ReadyQueue.erase(It);
- return IR;
-}
-
-void Scheduler::updatePendingQueue(SmallVectorImpl<InstRef> &Ready) {
- // Notify to instructions in the pending queue that a new cycle just
- // started.
- for (QueueEntryTy Entry : WaitQueue)
- Entry.second->cycleEvent();
- promoteToReadyQueue(Ready);
-}
-
-void Scheduler::updateIssuedQueue(SmallVectorImpl<InstRef> &Executed) {
- for (auto I = IssuedQueue.begin(), E = IssuedQueue.end(); I != E;) {
- const QueueEntryTy Entry = *I;
- Instruction *IS = Entry.second;
- IS->cycleEvent();
- if (IS->isExecuted()) {
- Executed.push_back({Entry.first, Entry.second});
- auto ToRemove = I;
- ++I;
- IssuedQueue.erase(ToRemove);
- } else {
- LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << Entry.first
- << " is still executing.\n");
- ++I;
- }
- }
-}
-
-void Scheduler::onInstructionExecuted(const InstRef &IR) {
- LSU->onInstructionExecuted(IR);
-}
-
-void Scheduler::reclaimSimulatedResources(SmallVectorImpl<ResourceRef> &Freed) {
- Resources->cycleEvent(Freed);
-}
-
-bool Scheduler::reserveResources(InstRef &IR) {
- // If necessary, reserve queue entries in the load-store unit (LSU).
- const bool Reserved = LSU->reserve(IR);
- if (!IR.getInstruction()->isReady() || (Reserved && !LSU->isReady(IR))) {
- LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the Wait Queue\n");
- WaitQueue[IR.getSourceIndex()] = IR.getInstruction();
- return false;
- }
- return true;
-}
-
-bool Scheduler::issueImmediately(InstRef &IR) {
- const InstrDesc &Desc = IR.getInstruction()->getDesc();
- if (!Desc.isZeroLatency() && !Resources->mustIssueImmediately(Desc)) {
- LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR
- << " to the Ready Queue\n");
- ReadyQueue[IR.getSourceIndex()] = IR.getInstruction();
- return false;
- }
- return true;
-}
-
-} // namespace mca
diff --git a/tools/llvm-mca/Scheduler.h b/tools/llvm-mca/Scheduler.h
deleted file mode 100644
index 428fbc01707d..000000000000
--- a/tools/llvm-mca/Scheduler.h
+++ /dev/null
@@ -1,515 +0,0 @@
-//===--------------------- Scheduler.h ------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// A scheduler for Processor Resource Units and Processor Resource Groups.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_SCHEDULER_H
-#define LLVM_TOOLS_LLVM_MCA_SCHEDULER_H
-
-#include "HWEventListener.h"
-#include "HardwareUnit.h"
-#include "Instruction.h"
-#include "LSUnit.h"
-#include "RetireControlUnit.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/MC/MCSubtargetInfo.h"
-#include <map>
-
-namespace mca {
-
-/// Used to notify the internal state of a processor resource.
-///
-/// A processor resource is available if it is not reserved, and there are
-/// available slots in the buffer. A processor resource is unavailable if it
-/// is either reserved, or the associated buffer is full. A processor resource
-/// with a buffer size of -1 is always available if it is not reserved.
-///
-/// Values of type ResourceStateEvent are returned by method
-/// ResourceState::isBufferAvailable(), which is used to query the internal
-/// state of a resource.
-///
-/// The naming convention for resource state events is:
-/// * Event names start with prefix RS_
-/// * Prefix RS_ is followed by a string describing the actual resource state.
-enum ResourceStateEvent {
- RS_BUFFER_AVAILABLE,
- RS_BUFFER_UNAVAILABLE,
- RS_RESERVED
-};
-
-/// A descriptor for processor resources.
-///
-/// Each object of class ResourceState is associated to a specific processor
-/// resource. There is an instance of this class for every processor resource
-/// defined by the scheduling model.
-/// A ResourceState dynamically tracks the availability of units of a processor
-/// resource. For example, the ResourceState of a ProcResGroup tracks the
-/// availability of resource units which are part of the group.
-///
-/// Internally, ResourceState uses a round-robin selector to identify
-/// which unit of the group shall be used next.
-class ResourceState {
- // Index to the MCProcResourceDesc in the processor Model.
- unsigned ProcResourceDescIndex;
- // A resource mask. This is generated by the tool with the help of
- // function `mca::createProcResourceMasks' (see Support.h).
- uint64_t ResourceMask;
-
- // A ProcResource can specify a number of units. For the purpose of dynamic
- // scheduling, a processor resource with more than one unit behaves like a
- // group. This field has one bit set for every unit/resource that is part of
- // the group.
- // For groups, this field defaults to 'ResourceMask'. For non-group
- // resources, the number of bits set in this mask is equivalent to the
- // number of units (i.e. field 'NumUnits' in 'ProcResourceUnits').
- uint64_t ResourceSizeMask;
-
- // A simple round-robin selector for processor resources.
- // Each bit of the mask identifies a sub resource within this group.
- //
- // As an example, lets assume that this ResourceState describes a
- // processor resource group composed of the following three units:
- // ResourceA -- 0b001
- // ResourceB -- 0b010
- // ResourceC -- 0b100
- //
- // Each unit is identified by a ResourceMask which always contains a
- // single bit set. Field NextInSequenceMask is initially set to value
- // 0xb111. That value is obtained by OR'ing the resource masks of
- // processor resource that are part of the group.
- //
- // NextInSequenceMask -- 0b111
- //
- // Field NextInSequenceMask is used by the resource manager (i.e.
- // an object of class ResourceManager) to select the "next available resource"
- // from the set. The algorithm would prioritize resources with a bigger
- // ResourceMask value.
- //
- // In this example, there are three resources in the set, and 'ResourceC'
- // has the highest mask value. The round-robin selector would firstly select
- // 'ResourceC', then 'ResourceB', and eventually 'ResourceA'.
- //
- // When a resource R is used, its corresponding bit is cleared from the set.
- //
- // Back to the example:
- // If 'ResourceC' is selected, then the new value of NextInSequenceMask
- // becomes 0xb011.
- //
- // When NextInSequenceMask becomes zero, it is reset to its original value
- // (in this example, that value would be 0b111).
- uint64_t NextInSequenceMask;
-
- // Some instructions can only be issued on very specific pipeline resources.
- // For those instructions, we know exactly which resource would be consumed
- // without having to dynamically select it using field 'NextInSequenceMask'.
- //
- // The resource mask bit associated to the (statically) selected
- // processor resource is still cleared from the 'NextInSequenceMask'.
- // If that bit was already zero in NextInSequenceMask, then we update
- // mask 'RemovedFromNextInSequence'.
- //
- // When NextInSequenceMask is reset back to its initial value, the algorithm
- // removes any bits which are set in RemoveFromNextInSequence.
- uint64_t RemovedFromNextInSequence;
-
- // A mask of ready units.
- uint64_t ReadyMask;
-
- // Buffered resources will have this field set to a positive number bigger
- // than 0. A buffered resource behaves like a separate reservation station
- // implementing its own buffer for out-of-order execution.
- // A buffer of 1 is for units that force in-order execution.
- // A value of 0 is treated specially. In particular, a resource with
- // A BufferSize = 0 is for an in-order issue/dispatch resource.
- // That means, this resource is reserved starting from the dispatch event,
- // until all the "resource cycles" are consumed after the issue event.
- // While this resource is reserved, no other instruction may be dispatched.
- int BufferSize;
-
- // Available slots in the buffer (zero, if this is not a buffered resource).
- unsigned AvailableSlots;
-
- // True if this is resource is currently unavailable.
- // An instruction may "reserve" a resource for a number of cycles.
- // During those cycles, the reserved resource cannot be used for other
- // instructions, even if the ReadyMask is set.
- bool Unavailable;
-
- bool isSubResourceReady(uint64_t ID) const { return ReadyMask & ID; }
-
- /// Returns the mask identifier of the next available resource in the set.
- uint64_t getNextInSequence() const {
- assert(NextInSequenceMask);
- return llvm::PowerOf2Floor(NextInSequenceMask);
- }
-
- /// Returns the mask of the next available resource within the set,
- /// and updates the resource selector.
- void updateNextInSequence() {
- NextInSequenceMask ^= getNextInSequence();
- if (!NextInSequenceMask)
- NextInSequenceMask = ResourceSizeMask;
- }
-
- uint64_t computeResourceSizeMaskForGroup(uint64_t ResourceMask) {
- assert(llvm::countPopulation(ResourceMask) > 1);
- return ResourceMask ^ llvm::PowerOf2Floor(ResourceMask);
- }
-
-public:
- ResourceState(const llvm::MCProcResourceDesc &Desc, unsigned Index,
- uint64_t Mask)
- : ProcResourceDescIndex(Index), ResourceMask(Mask) {
- bool IsAGroup = llvm::countPopulation(ResourceMask) > 1;
- ResourceSizeMask = IsAGroup ? computeResourceSizeMaskForGroup(ResourceMask)
- : ((1ULL << Desc.NumUnits) - 1);
- NextInSequenceMask = ResourceSizeMask;
- RemovedFromNextInSequence = 0;
- ReadyMask = ResourceSizeMask;
- BufferSize = Desc.BufferSize;
- AvailableSlots = BufferSize == -1 ? 0U : static_cast<unsigned>(BufferSize);
- Unavailable = false;
- }
-
- unsigned getProcResourceID() const { return ProcResourceDescIndex; }
- uint64_t getResourceMask() const { return ResourceMask; }
- int getBufferSize() const { return BufferSize; }
-
- bool isBuffered() const { return BufferSize > 0; }
- bool isInOrder() const { return BufferSize == 1; }
- bool isADispatchHazard() const { return BufferSize == 0; }
- bool isReserved() const { return Unavailable; }
-
- void setReserved() { Unavailable = true; }
- void clearReserved() { Unavailable = false; }
-
- // A resource is ready if it is not reserved, and if there are enough
- // available units.
- // If a resource is also a dispatch hazard, then we don't check if
- // it is reserved because that check would always return true.
- // A resource marked as "dispatch hazard" is always reserved at
- // dispatch time. When this method is called, the assumption is that
- // the user of this resource has been already dispatched.
- bool isReady(unsigned NumUnits = 1) const {
- return (!isReserved() || isADispatchHazard()) &&
- llvm::countPopulation(ReadyMask) >= NumUnits;
- }
- bool isAResourceGroup() const {
- return llvm::countPopulation(ResourceMask) > 1;
- }
-
- bool containsResource(uint64_t ID) const { return ResourceMask & ID; }
-
- void markSubResourceAsUsed(uint64_t ID) {
- assert(isSubResourceReady(ID));
- ReadyMask ^= ID;
- }
-
- void releaseSubResource(uint64_t ID) {
- assert(!isSubResourceReady(ID));
- ReadyMask ^= ID;
- }
-
- unsigned getNumUnits() const {
- return isAResourceGroup() ? 1U : llvm::countPopulation(ResourceSizeMask);
- }
-
- uint64_t selectNextInSequence();
- void removeFromNextInSequence(uint64_t ID);
-
- ResourceStateEvent isBufferAvailable() const {
- if (isADispatchHazard() && isReserved())
- return RS_RESERVED;
- if (!isBuffered() || AvailableSlots)
- return RS_BUFFER_AVAILABLE;
- return RS_BUFFER_UNAVAILABLE;
- }
-
- void reserveBuffer() {
- if (AvailableSlots)
- AvailableSlots--;
- }
-
- void releaseBuffer() {
- if (BufferSize > 0)
- AvailableSlots++;
- assert(AvailableSlots <= static_cast<unsigned>(BufferSize));
- }
-
-#ifndef NDEBUG
- void dump() const;
-#endif
-};
-
-/// A resource unit identifier.
-///
-/// This is used to identify a specific processor resource unit using a pair
-/// of indices where the 'first' index is a processor resource mask, and the
-/// 'second' index is an index for a "sub-resource" (i.e. unit).
-typedef std::pair<uint64_t, uint64_t> ResourceRef;
-
-// First: a MCProcResourceDesc index identifying a buffered resource.
-// Second: max number of buffer entries used in this resource.
-typedef std::pair<unsigned, unsigned> BufferUsageEntry;
-
-/// A resource manager for processor resource units and groups.
-///
-/// This class owns all the ResourceState objects, and it is responsible for
-/// acting on requests from a Scheduler by updating the internal state of
-/// ResourceState objects.
-/// This class doesn't know about instruction itineraries and functional units.
-/// In future, it can be extended to support itineraries too through the same
-/// public interface.
-class ResourceManager {
- // The resource manager owns all the ResourceState.
- using UniqueResourceState = std::unique_ptr<ResourceState>;
- llvm::SmallDenseMap<uint64_t, UniqueResourceState> Resources;
-
- // Keeps track of which resources are busy, and how many cycles are left
- // before those become usable again.
- llvm::SmallDenseMap<ResourceRef, unsigned> BusyResources;
-
- // A table to map processor resource IDs to processor resource masks.
- llvm::SmallVector<uint64_t, 8> ProcResID2Mask;
-
- // Adds a new resource state in Resources, as well as a new descriptor in
- // ResourceDescriptor.
- void addResource(const llvm::MCProcResourceDesc &Desc, unsigned Index,
- uint64_t Mask);
-
- // Populate resource descriptors.
- void initialize(const llvm::MCSchedModel &SM);
-
- // Returns the actual resource unit that will be used.
- ResourceRef selectPipe(uint64_t ResourceID);
-
- void use(ResourceRef RR);
- void release(ResourceRef RR);
-
- unsigned getNumUnits(uint64_t ResourceID) const {
- assert(Resources.find(ResourceID) != Resources.end());
- return Resources.find(ResourceID)->getSecond()->getNumUnits();
- }
-
- // Reserve a specific Resource kind.
- void reserveBuffer(uint64_t ResourceID) {
- assert(isBufferAvailable(ResourceID) ==
- ResourceStateEvent::RS_BUFFER_AVAILABLE);
- ResourceState &Resource = *Resources[ResourceID];
- Resource.reserveBuffer();
- }
-
- void releaseBuffer(uint64_t ResourceID) {
- Resources[ResourceID]->releaseBuffer();
- }
-
- ResourceStateEvent isBufferAvailable(uint64_t ResourceID) const {
- const ResourceState &Resource = *Resources.find(ResourceID)->second;
- return Resource.isBufferAvailable();
- }
-
- bool isReady(uint64_t ResourceID, unsigned NumUnits) const {
- const ResourceState &Resource = *Resources.find(ResourceID)->second;
- return Resource.isReady(NumUnits);
- }
-
-public:
- ResourceManager(const llvm::MCSchedModel &SM)
- : ProcResID2Mask(SM.getNumProcResourceKinds()) {
- initialize(SM);
- }
-
- // Returns RS_BUFFER_AVAILABLE if buffered resources are not reserved, and if
- // there are enough available slots in the buffers.
- ResourceStateEvent canBeDispatched(llvm::ArrayRef<uint64_t> Buffers) const;
-
- // Return the processor resource identifier associated to this Mask.
- unsigned resolveResourceMask(uint64_t Mask) const {
- return Resources.find(Mask)->second->getProcResourceID();
- }
-
- // Consume a slot in every buffered resource from array 'Buffers'. Resource
- // units that are dispatch hazards (i.e. BufferSize=0) are marked as reserved.
- void reserveBuffers(llvm::ArrayRef<uint64_t> Buffers);
-
- // Release buffer entries previously allocated by method reserveBuffers.
- void releaseBuffers(llvm::ArrayRef<uint64_t> Buffers);
-
- void reserveResource(uint64_t ResourceID) {
- ResourceState &Resource = *Resources[ResourceID];
- assert(!Resource.isReserved());
- Resource.setReserved();
- }
-
- void releaseResource(uint64_t ResourceID) {
- ResourceState &Resource = *Resources[ResourceID];
- Resource.clearReserved();
- }
-
- // Returns true if all resources are in-order, and there is at least one
- // resource which is a dispatch hazard (BufferSize = 0).
- bool mustIssueImmediately(const InstrDesc &Desc);
-
- bool canBeIssued(const InstrDesc &Desc) const;
-
- void issueInstruction(
- const InstrDesc &Desc,
- llvm::SmallVectorImpl<std::pair<ResourceRef, double>> &Pipes);
-
- void cycleEvent(llvm::SmallVectorImpl<ResourceRef> &ResourcesFreed);
-
-#ifndef NDEBUG
- void dump() const {
- for (const std::pair<uint64_t, UniqueResourceState> &Resource : Resources)
- Resource.second->dump();
- }
-#endif
-}; // namespace mca
-
-/// Class Scheduler is responsible for issuing instructions to pipeline
-/// resources. Internally, it delegates to a ResourceManager the management of
-/// processor resources.
-/// This class is also responsible for tracking the progress of instructions
-/// from the dispatch stage, until the write-back stage.
-///
-/// An nstruction dispatched to the Scheduler is initially placed into either
-/// the 'WaitQueue' or the 'ReadyQueue' depending on the availability of the
-/// input operands. Instructions in the WaitQueue are ordered by instruction
-/// index. An instruction is moved from the WaitQueue to the ReadyQueue when
-/// register operands become available, and all memory dependencies are met.
-/// Instructions that are moved from the WaitQueue to the ReadyQueue transition
-/// from state 'IS_AVAILABLE' to state 'IS_READY'.
-///
-/// At the beginning of each cycle, the Scheduler checks if there are
-/// instructions in the WaitQueue that can be moved to the ReadyQueue. If the
-/// ReadyQueue is not empty, then older instructions from the queue are issued
-/// to the processor pipelines, and the underlying ResourceManager is updated
-/// accordingly. The ReadyQueue is ordered by instruction index to guarantee
-/// that the first instructions in the set are also the oldest.
-///
-/// An Instruction is moved from the ReadyQueue the `IssuedQueue` when it is
-/// issued to a (one or more) pipeline(s). This event also causes an instruction
-/// state transition (i.e. from state IS_READY, to state IS_EXECUTING).
-/// An Instruction leaves the IssuedQueue when it reaches the write-back stage.
-class Scheduler : public HardwareUnit {
- const llvm::MCSchedModel &SM;
-
- // Hardware resources that are managed by this scheduler.
- std::unique_ptr<ResourceManager> Resources;
- std::unique_ptr<LSUnit> LSU;
-
- using QueueEntryTy = std::pair<unsigned, Instruction *>;
- std::map<unsigned, Instruction *> WaitQueue;
- std::map<unsigned, Instruction *> ReadyQueue;
- std::map<unsigned, Instruction *> IssuedQueue;
-
- /// Issue an instruction without updating the ready queue.
- void issueInstructionImpl(
- InstRef &IR,
- llvm::SmallVectorImpl<std::pair<ResourceRef, double>> &Pipes);
-
-public:
- Scheduler(const llvm::MCSchedModel &Model, unsigned LoadQueueSize,
- unsigned StoreQueueSize, bool AssumeNoAlias)
- : SM(Model), Resources(llvm::make_unique<ResourceManager>(SM)),
- LSU(llvm::make_unique<LSUnit>(LoadQueueSize, StoreQueueSize,
- AssumeNoAlias)) {}
-
- /// Check if the instruction in 'IR' can be dispatched.
- ///
- /// The DispatchStage is responsible for querying the Scheduler before
- /// dispatching new instructions. This routine is used for performing such
- /// a query. If the instruction 'IR' can be dispatched, then true is
- /// returned, otherwise false is returned with Event set to the stall type.
- bool canBeDispatched(const InstRef &IR,
- HWStallEvent::GenericEventType &Event) const;
-
- /// Returns true if there is availibility for IR in the LSU.
- bool isReady(const InstRef &IR) const { return LSU->isReady(IR); }
-
- /// Issue an instruction. The Used container is populated with
- /// the resource objects consumed on behalf of issuing this instruction.
- void
- issueInstruction(InstRef &IR,
- llvm::SmallVectorImpl<std::pair<ResourceRef, double>> &Used);
-
- /// This routine will attempt to issue an instruction immediately (for
- /// zero-latency instructions).
- ///
- /// Returns true if the instruction is issued immediately. If this does not
- /// occur, then the instruction will be added to the Scheduler's ReadyQueue.
- bool issueImmediately(InstRef &IR);
-
- /// Reserve one entry in each buffered resource.
- void reserveBuffers(llvm::ArrayRef<uint64_t> Buffers) {
- Resources->reserveBuffers(Buffers);
- }
-
- /// Release buffer entries previously allocated by method reserveBuffers.
- void releaseBuffers(llvm::ArrayRef<uint64_t> Buffers) {
- Resources->releaseBuffers(Buffers);
- }
-
- /// Update the resources managed by the scheduler.
- /// This routine is to be called at the start of a new cycle, and is
- /// responsible for updating scheduler resources. Resources are released
- /// once they have been fully consumed.
- void reclaimSimulatedResources(llvm::SmallVectorImpl<ResourceRef> &Freed);
-
- /// Move instructions from the WaitQueue to the ReadyQueue if input operands
- /// are all available.
- void promoteToReadyQueue(llvm::SmallVectorImpl<InstRef> &Ready);
-
- /// Update the ready queue.
- void updatePendingQueue(llvm::SmallVectorImpl<InstRef> &Ready);
-
- /// Update the issued queue.
- void updateIssuedQueue(llvm::SmallVectorImpl<InstRef> &Executed);
-
- /// Updates the Scheduler's resources to reflect that an instruction has just
- /// been executed.
- void onInstructionExecuted(const InstRef &IR);
-
- /// Obtain the processor's resource identifier for the given
- /// resource mask.
- unsigned getResourceID(uint64_t Mask) {
- return Resources->resolveResourceMask(Mask);
- }
-
- /// Reserve resources necessary to issue the instruction.
- /// Returns true if the resources are ready and the (LSU) can
- /// execute the given instruction immediately.
- bool reserveResources(InstRef &IR);
-
- /// Select the next instruction to issue from the ReadyQueue.
- /// This method gives priority to older instructions.
- InstRef select();
-
-#ifndef NDEBUG
- // Update the ready queues.
- void dump() const;
-
- // This routine performs a sanity check. This routine should only be called
- // when we know that 'IR' is not in the scheduler's instruction queues.
- void sanityCheck(const InstRef &IR) const {
- const unsigned Idx = IR.getSourceIndex();
- assert(WaitQueue.find(Idx) == WaitQueue.end());
- assert(ReadyQueue.find(Idx) == ReadyQueue.end());
- assert(IssuedQueue.find(Idx) == IssuedQueue.end());
- }
-#endif // !NDEBUG
-};
-} // namespace mca
-
-#endif // LLVM_TOOLS_LLVM_MCA_SCHEDULER_H
diff --git a/tools/llvm-mca/SchedulerStatistics.cpp b/tools/llvm-mca/SchedulerStatistics.cpp
deleted file mode 100644
index 5c6d22a71812..000000000000
--- a/tools/llvm-mca/SchedulerStatistics.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-//===--------------------- SchedulerStatistics.cpp --------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file implements the SchedulerStatistics interface.
-///
-//===----------------------------------------------------------------------===//
-
-#include "SchedulerStatistics.h"
-#include "llvm/Support/Format.h"
-
-using namespace llvm;
-
-namespace mca {
-
-void SchedulerStatistics::onEvent(const HWInstructionEvent &Event) {
- if (Event.Type == HWInstructionEvent::Issued)
- ++NumIssued;
-}
-
-void SchedulerStatistics::onReservedBuffers(ArrayRef<unsigned> Buffers) {
- for (const unsigned Buffer : Buffers) {
- if (BufferedResources.find(Buffer) != BufferedResources.end()) {
- BufferUsage &BU = BufferedResources[Buffer];
- BU.SlotsInUse++;
- BU.MaxUsedSlots = std::max(BU.MaxUsedSlots, BU.SlotsInUse);
- continue;
- }
-
- BufferedResources.insert(
- std::pair<unsigned, BufferUsage>(Buffer, {1U, 1U}));
- }
-}
-
-void SchedulerStatistics::onReleasedBuffers(ArrayRef<unsigned> Buffers) {
- for (const unsigned Buffer : Buffers) {
- assert(BufferedResources.find(Buffer) != BufferedResources.end() &&
- "Buffered resource not in map?");
- BufferUsage &BU = BufferedResources[Buffer];
- BU.SlotsInUse--;
- }
-}
-
-void SchedulerStatistics::printSchedulerStatistics(
- llvm::raw_ostream &OS) const {
- std::string Buffer;
- raw_string_ostream TempStream(Buffer);
- TempStream << "\n\nSchedulers - number of cycles where we saw N instructions "
- "issued:\n";
- TempStream << "[# issued], [# cycles]\n";
- for (const std::pair<unsigned, unsigned> &Entry : IssuedPerCycle) {
- TempStream << " " << Entry.first << ", " << Entry.second << " ("
- << format("%.1f", ((double)Entry.second / NumCycles) * 100)
- << "%)\n";
- }
-
- TempStream.flush();
- OS << Buffer;
-}
-
-void SchedulerStatistics::printSchedulerUsage(raw_ostream &OS) const {
- std::string Buffer;
- raw_string_ostream TempStream(Buffer);
- TempStream << "\n\nScheduler's queue usage:\n";
- // Early exit if no buffered resources were consumed.
- if (BufferedResources.empty()) {
- TempStream << "No scheduler resources used.\n";
- TempStream.flush();
- OS << Buffer;
- return;
- }
-
- for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
- const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
- if (ProcResource.BufferSize <= 0)
- continue;
-
- const auto It = BufferedResources.find(I);
- unsigned MaxUsedSlots =
- It == BufferedResources.end() ? 0 : It->second.MaxUsedSlots;
- TempStream << ProcResource.Name << ", " << MaxUsedSlots << '/'
- << ProcResource.BufferSize << '\n';
- }
-
- TempStream.flush();
- OS << Buffer;
-}
-} // namespace mca
diff --git a/tools/llvm-mca/SourceMgr.h b/tools/llvm-mca/SourceMgr.h
deleted file mode 100644
index 15a85a69569f..000000000000
--- a/tools/llvm-mca/SourceMgr.h
+++ /dev/null
@@ -1,63 +0,0 @@
-//===--------------------- SourceMgr.h --------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-/// This file implements class SourceMgr. Class SourceMgr abstracts the input
-/// code sequence (a sequence of MCInst), and assings unique identifiers to
-/// every instruction in the sequence.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_SOURCEMGR_H
-#define LLVM_TOOLS_LLVM_MCA_SOURCEMGR_H
-
-#include "llvm/MC/MCInst.h"
-#include <vector>
-
-namespace mca {
-
-typedef std::pair<unsigned, const llvm::MCInst *> SourceRef;
-
-class SourceMgr {
- using InstVec = std::vector<std::unique_ptr<const llvm::MCInst>>;
- const InstVec &Sequence;
- unsigned Current;
- unsigned Iterations;
- static const unsigned DefaultIterations = 100;
-
-public:
- SourceMgr(const InstVec &MCInstSequence, unsigned NumIterations)
- : Sequence(MCInstSequence), Current(0),
- Iterations(NumIterations ? NumIterations : DefaultIterations) {}
-
- unsigned getCurrentIteration() const { return Current / Sequence.size(); }
- unsigned getNumIterations() const { return Iterations; }
- unsigned size() const { return Sequence.size(); }
- const InstVec &getSequence() const { return Sequence; }
-
- bool hasNext() const { return Current < (Iterations * size()); }
- void updateNext() { Current++; }
-
- const SourceRef peekNext() const {
- unsigned Index = getCurrentInstructionIndex();
- return SourceRef(Current, Sequence[Index].get());
- }
-
- unsigned getCurrentInstructionIndex() const {
- return Current % Sequence.size();
- }
-
- const llvm::MCInst &getMCInstFromIndex(unsigned Index) const {
- return *Sequence[Index % size()];
- }
-
- bool isEmpty() const { return size() == 0; }
-};
-} // namespace mca
-
-#endif
diff --git a/tools/llvm-mca/Stage.cpp b/tools/llvm-mca/Stage.cpp
deleted file mode 100644
index 7ead940e63c1..000000000000
--- a/tools/llvm-mca/Stage.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-//===---------------------- Stage.cpp ---------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines a stage.
-/// A chain of stages compose an instruction pipeline.
-///
-//===----------------------------------------------------------------------===//
-
-#include "Stage.h"
-
-namespace mca {
-
-// Pin the vtable here in the implementation file.
-Stage::Stage() {}
-
-void Stage::addListener(HWEventListener *Listener) {
- Listeners.insert(Listener);
-}
-
-} // namespace mca
diff --git a/tools/llvm-mca/Stage.h b/tools/llvm-mca/Stage.h
deleted file mode 100644
index 9dbdcd89a33b..000000000000
--- a/tools/llvm-mca/Stage.h
+++ /dev/null
@@ -1,76 +0,0 @@
-//===---------------------- Stage.h -----------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines a stage.
-/// A chain of stages compose an instruction pipeline.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_STAGE_H
-#define LLVM_TOOLS_LLVM_MCA_STAGE_H
-
-#include "HWEventListener.h"
-#include <set>
-
-namespace mca {
-
-class InstRef;
-
-class Stage {
- Stage(const Stage &Other) = delete;
- Stage &operator=(const Stage &Other) = delete;
- std::set<HWEventListener *> Listeners;
-
-protected:
- const std::set<HWEventListener *> &getListeners() const { return Listeners; }
-
-public:
- Stage();
- virtual ~Stage() = default;
-
- /// Called prior to preExecute to ensure that the stage has items that it
- /// is to process. For example, a FetchStage might have more instructions
- /// that need to be processed, or a RCU might have items that have yet to
- /// retire.
- virtual bool hasWorkToComplete() const = 0;
-
- /// Called once at the start of each cycle. This can be used as a setup
- /// phase to prepare for the executions during the cycle.
- virtual void cycleStart() {}
-
- /// Called once at the end of each cycle.
- virtual void cycleEnd() {}
-
- /// Called prior to executing the list of stages.
- /// This can be called multiple times per cycle.
- virtual void preExecute() {}
-
- /// Called as a cleanup and finalization phase after each execution.
- /// This will only be called if all stages return a success from their
- /// execute callback. This can be called multiple times per cycle.
- virtual void postExecute() {}
-
- /// The primary action that this stage performs.
- /// Returning false prevents successor stages from having their 'execute'
- /// routine called. This can be called multiple times during a single cycle.
- virtual bool execute(InstRef &IR) = 0;
-
- /// Add a listener to receive callbacks during the execution of this stage.
- void addListener(HWEventListener *Listener);
-
- /// Notify listeners of a particular hardware event.
- template <typename EventT> void notifyEvent(const EventT &Event) {
- for (HWEventListener *Listener : Listeners)
- Listener->onEvent(Event);
- }
-};
-
-} // namespace mca
-#endif // LLVM_TOOLS_LLVM_MCA_STAGE_H
diff --git a/tools/llvm-mca/Support.cpp b/tools/llvm-mca/Support.cpp
deleted file mode 100644
index 8f6b8a91f38f..000000000000
--- a/tools/llvm-mca/Support.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-//===--------------------- Support.cpp --------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file implements a few helper functions used by various pipeline
-/// components.
-///
-//===----------------------------------------------------------------------===//
-
-#include "Support.h"
-#include "llvm/MC/MCSchedule.h"
-
-namespace mca {
-
-using namespace llvm;
-
-void computeProcResourceMasks(const MCSchedModel &SM,
- SmallVectorImpl<uint64_t> &Masks) {
- unsigned ProcResourceID = 0;
-
- // Create a unique bitmask for every processor resource unit.
- // Skip resource at index 0, since it always references 'InvalidUnit'.
- Masks.resize(SM.getNumProcResourceKinds());
- for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
- const MCProcResourceDesc &Desc = *SM.getProcResource(I);
- if (Desc.SubUnitsIdxBegin)
- continue;
- Masks[I] = 1ULL << ProcResourceID;
- ProcResourceID++;
- }
-
- // Create a unique bitmask for every processor resource group.
- for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
- const MCProcResourceDesc &Desc = *SM.getProcResource(I);
- if (!Desc.SubUnitsIdxBegin)
- continue;
- Masks[I] = 1ULL << ProcResourceID;
- for (unsigned U = 0; U < Desc.NumUnits; ++U) {
- uint64_t OtherMask = Masks[Desc.SubUnitsIdxBegin[U]];
- Masks[I] |= OtherMask;
- }
- ProcResourceID++;
- }
-}
-
-double computeBlockRThroughput(const MCSchedModel &SM, unsigned DispatchWidth,
- unsigned NumMicroOps,
- ArrayRef<unsigned> ProcResourceUsage) {
- // The block throughput is bounded from above by the hardware dispatch
- // throughput. That is because the DispatchWidth is an upper bound on the
- // number of opcodes that can be part of a single dispatch group.
- double Max = static_cast<double>(NumMicroOps) / DispatchWidth;
-
- // The block throughput is also limited by the amount of hardware parallelism.
- // The number of available resource units affects the resource pressure
- // distribution, as well as how many blocks can be executed every cycle.
- for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
- unsigned ResourceCycles = ProcResourceUsage[I];
- if (!ResourceCycles)
- continue;
-
- const MCProcResourceDesc &MCDesc = *SM.getProcResource(I);
- double Throughput = static_cast<double>(ResourceCycles) / MCDesc.NumUnits;
- Max = std::max(Max, Throughput);
- }
-
- // The block reciprocal throughput is computed as the MAX of:
- // - (NumMicroOps / DispatchWidth)
- // - (NumUnits / ResourceCycles) for every consumed processor resource.
- return Max;
-}
-
-} // namespace mca
diff --git a/tools/llvm-mca/Support.h b/tools/llvm-mca/Support.h
deleted file mode 100644
index fd8d8b5a23b3..000000000000
--- a/tools/llvm-mca/Support.h
+++ /dev/null
@@ -1,58 +0,0 @@
-//===--------------------- Support.h ----------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \file
-///
-/// Helper functions used by various pipeline components.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVM_MCA_SUPPORT_H
-#define LLVM_TOOLS_LLVM_MCA_SUPPORT_H
-
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/MC/MCSchedule.h"
-
-namespace mca {
-
-/// Populates vector Masks with processor resource masks.
-///
-/// The number of bits set in a mask depends on the processor resource type.
-/// Each processor resource mask has at least one bit set. For groups, the
-/// number of bits set in the mask is equal to the cardinality of the group plus
-/// one. Excluding the most significant bit, the remaining bits in the mask
-/// identify processor resources that are part of the group.
-///
-/// Example:
-///
-/// ResourceA -- Mask: 0b001
-/// ResourceB -- Mask: 0b010
-/// ResourceAB -- Mask: 0b100 U (ResourceA::Mask | ResourceB::Mask) == 0b111
-///
-/// ResourceAB is a processor resource group containing ResourceA and ResourceB.
-/// Each resource mask uniquely identifies a resource; both ResourceA and
-/// ResourceB only have one bit set.
-/// ResourceAB is a group; excluding the most significant bit in the mask, the
-/// remaining bits identify the composition of the group.
-///
-/// Resource masks are used by the ResourceManager to solve set membership
-/// problems with simple bit manipulation operations.
-void computeProcResourceMasks(const llvm::MCSchedModel &SM,
- llvm::SmallVectorImpl<uint64_t> &Masks);
-
-/// Compute the reciprocal block throughput from a set of processor resource
-/// cycles. The reciprocal block throughput is computed as the MAX between:
-/// - NumMicroOps / DispatchWidth
-/// - ProcResourceCycles / #ProcResourceUnits (for every consumed resource).
-double computeBlockRThroughput(const llvm::MCSchedModel &SM,
- unsigned DispatchWidth, unsigned NumMicroOps,
- llvm::ArrayRef<unsigned> ProcResourceUsage);
-} // namespace mca
-
-#endif
diff --git a/tools/llvm-mca/TimelineView.cpp b/tools/llvm-mca/TimelineView.cpp
deleted file mode 100644
index 6e75cac0d432..000000000000
--- a/tools/llvm-mca/TimelineView.cpp
+++ /dev/null
@@ -1,240 +0,0 @@
-//===--------------------- TimelineView.cpp ---------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-/// \brief
-///
-/// This file implements the TimelineView interface.
-///
-//===----------------------------------------------------------------------===//
-
-#include "TimelineView.h"
-
-using namespace llvm;
-
-namespace mca {
-
-void TimelineView::initialize(unsigned MaxIterations) {
- unsigned NumInstructions =
- AsmSequence.getNumIterations() * AsmSequence.size();
- if (!MaxIterations)
- MaxIterations = DEFAULT_ITERATIONS;
- unsigned NumEntries =
- std::min(NumInstructions, MaxIterations * AsmSequence.size());
- Timeline.resize(NumEntries);
- TimelineViewEntry NullTVEntry = {0, 0, 0, 0, 0};
- std::fill(Timeline.begin(), Timeline.end(), NullTVEntry);
-
- WaitTime.resize(AsmSequence.size());
- WaitTimeEntry NullWTEntry = {0, 0, 0, 0};
- std::fill(WaitTime.begin(), WaitTime.end(), NullWTEntry);
-}
-
-void TimelineView::onEvent(const HWInstructionEvent &Event) {
- const unsigned Index = Event.IR.getSourceIndex();
- if (CurrentCycle >= MaxCycle || Index >= Timeline.size())
- return;
- switch (Event.Type) {
- case HWInstructionEvent::Retired: {
- TimelineViewEntry &TVEntry = Timeline[Index];
- TVEntry.CycleRetired = CurrentCycle;
-
- // Update the WaitTime entry which corresponds to this Index.
- WaitTimeEntry &WTEntry = WaitTime[Index % AsmSequence.size()];
- WTEntry.Executions++;
- WTEntry.CyclesSpentInSchedulerQueue +=
- TVEntry.CycleIssued - TVEntry.CycleDispatched;
- assert(TVEntry.CycleDispatched <= TVEntry.CycleReady);
- WTEntry.CyclesSpentInSQWhileReady +=
- TVEntry.CycleIssued - TVEntry.CycleReady;
- WTEntry.CyclesSpentAfterWBAndBeforeRetire +=
- (TVEntry.CycleRetired - 1) - TVEntry.CycleExecuted;
- break;
- }
- case HWInstructionEvent::Ready:
- Timeline[Index].CycleReady = CurrentCycle;
- break;
- case HWInstructionEvent::Issued:
- Timeline[Index].CycleIssued = CurrentCycle;
- break;
- case HWInstructionEvent::Executed:
- Timeline[Index].CycleExecuted = CurrentCycle;
- break;
- case HWInstructionEvent::Dispatched:
- Timeline[Index].CycleDispatched = CurrentCycle;
- break;
- default:
- return;
- }
- LastCycle = std::max(LastCycle, CurrentCycle);
-}
-
-void TimelineView::printWaitTimeEntry(formatted_raw_ostream &OS,
- const WaitTimeEntry &Entry,
- unsigned SourceIndex) const {
- OS << SourceIndex << '.';
- OS.PadToColumn(7);
-
- if (Entry.Executions == 0) {
- OS << "- - - - ";
- } else {
- double AverageTime1, AverageTime2, AverageTime3;
- unsigned Executions = Entry.Executions;
- AverageTime1 = (double)Entry.CyclesSpentInSchedulerQueue / Executions;
- AverageTime2 = (double)Entry.CyclesSpentInSQWhileReady / Executions;
- AverageTime3 = (double)Entry.CyclesSpentAfterWBAndBeforeRetire / Executions;
-
- OS << Executions;
- OS.PadToColumn(13);
-
- OS << format("%.1f", floor((AverageTime1 * 10) + 0.5) / 10);
- OS.PadToColumn(20);
- OS << format("%.1f", floor((AverageTime2 * 10) + 0.5) / 10);
- OS.PadToColumn(27);
- OS << format("%.1f", floor((AverageTime3 * 10) + 0.5) / 10);
- OS.PadToColumn(34);
- }
-}
-
-void TimelineView::printAverageWaitTimes(raw_ostream &OS) const {
- if (WaitTime.empty())
- return;
-
- std::string Buffer;
- raw_string_ostream TempStream(Buffer);
- formatted_raw_ostream FOS(TempStream);
-
- FOS << "\n\nAverage Wait times (based on the timeline view):\n"
- << "[0]: Executions\n"
- << "[1]: Average time spent waiting in a scheduler's queue\n"
- << "[2]: Average time spent waiting in a scheduler's queue while ready\n"
- << "[3]: Average time elapsed from WB until retire stage\n\n";
- FOS << " [0] [1] [2] [3]\n";
-
- // Use a different string stream for the instruction.
- std::string Instruction;
- raw_string_ostream InstrStream(Instruction);
-
- for (unsigned I = 0, E = WaitTime.size(); I < E; ++I) {
- printWaitTimeEntry(FOS, WaitTime[I], I);
- // Append the instruction info at the end of the line.
- const MCInst &Inst = AsmSequence.getMCInstFromIndex(I);
-
- MCIP.printInst(&Inst, InstrStream, "", STI);
- InstrStream.flush();
-
- // Consume any tabs or spaces at the beginning of the string.
- StringRef Str(Instruction);
- Str = Str.ltrim();
- FOS << " " << Str << '\n';
- FOS.flush();
- Instruction = "";
-
- OS << Buffer;
- Buffer = "";
- }
-}
-
-void TimelineView::printTimelineViewEntry(formatted_raw_ostream &OS,
- const TimelineViewEntry &Entry,
- unsigned Iteration,
- unsigned SourceIndex) const {
- if (Iteration == 0 && SourceIndex == 0)
- OS << '\n';
- OS << '[' << Iteration << ',' << SourceIndex << ']';
- OS.PadToColumn(10);
- for (unsigned I = 0, E = Entry.CycleDispatched; I < E; ++I)
- OS << ((I % 5 == 0) ? '.' : ' ');
- OS << TimelineView::DisplayChar::Dispatched;
- if (Entry.CycleDispatched != Entry.CycleExecuted) {
- // Zero latency instructions have the same value for CycleDispatched,
- // CycleIssued and CycleExecuted.
- for (unsigned I = Entry.CycleDispatched + 1, E = Entry.CycleIssued; I < E;
- ++I)
- OS << TimelineView::DisplayChar::Waiting;
- if (Entry.CycleIssued == Entry.CycleExecuted)
- OS << TimelineView::DisplayChar::DisplayChar::Executed;
- else {
- if (Entry.CycleDispatched != Entry.CycleIssued)
- OS << TimelineView::DisplayChar::Executing;
- for (unsigned I = Entry.CycleIssued + 1, E = Entry.CycleExecuted; I < E;
- ++I)
- OS << TimelineView::DisplayChar::Executing;
- OS << TimelineView::DisplayChar::Executed;
- }
- }
-
- for (unsigned I = Entry.CycleExecuted + 1, E = Entry.CycleRetired; I < E; ++I)
- OS << TimelineView::DisplayChar::RetireLag;
- OS << TimelineView::DisplayChar::Retired;
-
- // Skip other columns.
- for (unsigned I = Entry.CycleRetired + 1, E = LastCycle; I <= E; ++I)
- OS << ((I % 5 == 0 || I == LastCycle) ? '.' : ' ');
-}
-
-static void printTimelineHeader(formatted_raw_ostream &OS, unsigned Cycles) {
- OS << "\n\nTimeline view:\n";
- if (Cycles >= 10) {
- OS.PadToColumn(10);
- for (unsigned I = 0; I <= Cycles; ++I) {
- if (((I / 10) & 1) == 0)
- OS << ' ';
- else
- OS << I % 10;
- }
- OS << '\n';
- }
-
- OS << "Index";
- OS.PadToColumn(10);
- for (unsigned I = 0; I <= Cycles; ++I) {
- if (((I / 10) & 1) == 0)
- OS << I % 10;
- else
- OS << ' ';
- }
- OS << '\n';
-}
-
-void TimelineView::printTimeline(raw_ostream &OS) const {
- std::string Buffer;
- raw_string_ostream StringStream(Buffer);
- formatted_raw_ostream FOS(StringStream);
-
- printTimelineHeader(FOS, LastCycle);
- FOS.flush();
- OS << Buffer;
-
- // Use a different string stream for the instruction.
- std::string Instruction;
- raw_string_ostream InstrStream(Instruction);
-
- for (unsigned I = 0, E = Timeline.size(); I < E; ++I) {
- Buffer = "";
- const TimelineViewEntry &Entry = Timeline[I];
- if (Entry.CycleRetired == 0)
- return;
-
- unsigned Iteration = I / AsmSequence.size();
- unsigned SourceIndex = I % AsmSequence.size();
- printTimelineViewEntry(FOS, Entry, Iteration, SourceIndex);
- // Append the instruction info at the end of the line.
- const MCInst &Inst = AsmSequence.getMCInstFromIndex(I);
- MCIP.printInst(&Inst, InstrStream, "", STI);
- InstrStream.flush();
-
- // Consume any tabs or spaces at the beginning of the string.
- StringRef Str(Instruction);
- Str = Str.ltrim();
- FOS << " " << Str << '\n';
- FOS.flush();
- Instruction = "";
- OS << Buffer;
- }
-}
-} // namespace mca
diff --git a/tools/llvm-mca/Views/DispatchStatistics.cpp b/tools/llvm-mca/Views/DispatchStatistics.cpp
new file mode 100644
index 000000000000..2562c82407bf
--- /dev/null
+++ b/tools/llvm-mca/Views/DispatchStatistics.cpp
@@ -0,0 +1,86 @@
+//===--------------------- DispatchStatistics.cpp ---------------------*- C++
+//-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the DispatchStatistics interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Views/DispatchStatistics.h"
+#include "llvm/Support/Format.h"
+
+namespace llvm {
+namespace mca {
+
+void DispatchStatistics::onEvent(const HWStallEvent &Event) {
+ if (Event.Type < HWStallEvent::LastGenericEvent)
+ HWStalls[Event.Type]++;
+}
+
+void DispatchStatistics::onEvent(const HWInstructionEvent &Event) {
+ if (Event.Type != HWInstructionEvent::Dispatched)
+ return;
+
+ const auto &DE = static_cast<const HWInstructionDispatchedEvent &>(Event);
+ NumDispatched += DE.MicroOpcodes;
+}
+
+void DispatchStatistics::printDispatchHistogram(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ TempStream << "\n\nDispatch Logic - "
+ << "number of cycles where we saw N micro opcodes dispatched:\n";
+ TempStream << "[# dispatched], [# cycles]\n";
+ for (const std::pair<unsigned, unsigned> &Entry : DispatchGroupSizePerCycle) {
+ double Percentage = ((double)Entry.second / NumCycles) * 100.0;
+ TempStream << " " << Entry.first << ", " << Entry.second
+ << " (" << format("%.1f", floor((Percentage * 10) + 0.5) / 10)
+ << "%)\n";
+ }
+
+ TempStream.flush();
+ OS << Buffer;
+}
+
+static void printStalls(raw_ostream &OS, unsigned NumStalls,
+ unsigned NumCycles) {
+ if (!NumStalls) {
+ OS << NumStalls;
+ return;
+ }
+
+ double Percentage = ((double)NumStalls / NumCycles) * 100.0;
+ OS << NumStalls << " ("
+ << format("%.1f", floor((Percentage * 10) + 0.5) / 10) << "%)";
+}
+
+void DispatchStatistics::printDispatchStalls(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream SS(Buffer);
+ SS << "\n\nDynamic Dispatch Stall Cycles:\n";
+ SS << "RAT - Register unavailable: ";
+ printStalls(SS, HWStalls[HWStallEvent::RegisterFileStall], NumCycles);
+ SS << "\nRCU - Retire tokens unavailable: ";
+ printStalls(SS, HWStalls[HWStallEvent::RetireControlUnitStall], NumCycles);
+ SS << "\nSCHEDQ - Scheduler full: ";
+ printStalls(SS, HWStalls[HWStallEvent::SchedulerQueueFull], NumCycles);
+ SS << "\nLQ - Load queue full: ";
+ printStalls(SS, HWStalls[HWStallEvent::LoadQueueFull], NumCycles);
+ SS << "\nSQ - Store queue full: ";
+ printStalls(SS, HWStalls[HWStallEvent::StoreQueueFull], NumCycles);
+ SS << "\nGROUP - Static restrictions on the dispatch group: ";
+ printStalls(SS, HWStalls[HWStallEvent::DispatchGroupStall], NumCycles);
+ SS << '\n';
+ SS.flush();
+ OS << Buffer;
+}
+
+} // namespace mca
+} // namespace llvm
diff --git a/tools/llvm-mca/DispatchStatistics.h b/tools/llvm-mca/Views/DispatchStatistics.h
index 1e389d54766b..6679c81efe95 100644
--- a/tools/llvm-mca/DispatchStatistics.h
+++ b/tools/llvm-mca/Views/DispatchStatistics.h
@@ -24,7 +24,7 @@
/// GROUP - Static restrictions on the dispatch group: 0
///
///
-/// Dispatch Logic - number of cycles where we saw N instructions dispatched:
+/// Dispatch Logic - number of cycles where we saw N micro opcodes dispatched:
/// [# dispatched], [# cycles]
/// 0, 15 (11.5%)
/// 2, 4 (3.1%)
@@ -34,11 +34,12 @@
#ifndef LLVM_TOOLS_LLVM_MCA_DISPATCHVIEW_H
#define LLVM_TOOLS_LLVM_MCA_DISPATCHVIEW_H
-#include "View.h"
+#include "Views/View.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include <map>
+namespace llvm {
namespace mca {
class DispatchStatistics : public View {
@@ -80,5 +81,6 @@ public:
}
};
} // namespace mca
+} // namespace llvm
#endif
diff --git a/tools/llvm-mca/InstructionInfoView.cpp b/tools/llvm-mca/Views/InstructionInfoView.cpp
index 0e50a96d19c1..5016afb49e44 100644
--- a/tools/llvm-mca/InstructionInfoView.cpp
+++ b/tools/llvm-mca/Views/InstructionInfoView.cpp
@@ -12,17 +12,15 @@
///
//===----------------------------------------------------------------------===//
-#include "InstructionInfoView.h"
+#include "Views/InstructionInfoView.h"
+namespace llvm {
namespace mca {
-using namespace llvm;
-
void InstructionInfoView::printView(raw_ostream &OS) const {
std::string Buffer;
raw_string_ostream TempStream(Buffer);
const MCSchedModel &SM = STI.getSchedModel();
- unsigned Instructions = Source.size();
std::string Instruction;
raw_string_ostream InstrStream(Instruction);
@@ -32,8 +30,7 @@ void InstructionInfoView::printView(raw_ostream &OS) const {
<< "[4]: MayLoad\n[5]: MayStore\n[6]: HasSideEffects (U)\n\n";
TempStream << "[1] [2] [3] [4] [5] [6] Instructions:\n";
- for (unsigned I = 0, E = Instructions; I < E; ++I) {
- const MCInst &Inst = Source.getMCInstFromIndex(I);
+ for (const MCInst &Inst : Source) {
const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode());
// Obtain the scheduling class information from the instruction.
@@ -89,3 +86,4 @@ void InstructionInfoView::printView(raw_ostream &OS) const {
OS << Buffer;
}
} // namespace mca.
+} // namespace llvm
diff --git a/tools/llvm-mca/InstructionInfoView.h b/tools/llvm-mca/Views/InstructionInfoView.h
index 0770ae3d2b57..3ef95d474490 100644
--- a/tools/llvm-mca/InstructionInfoView.h
+++ b/tools/llvm-mca/Views/InstructionInfoView.h
@@ -35,8 +35,9 @@
#ifndef LLVM_TOOLS_LLVM_MCA_INSTRUCTIONINFOVIEW_H
#define LLVM_TOOLS_LLVM_MCA_INSTRUCTIONINFOVIEW_H
-#include "SourceMgr.h"
-#include "View.h"
+#include "Views/View.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
@@ -44,23 +45,25 @@
#define DEBUG_TYPE "llvm-mca"
+namespace llvm {
namespace mca {
/// A view that prints out generic instruction information.
class InstructionInfoView : public View {
const llvm::MCSubtargetInfo &STI;
const llvm::MCInstrInfo &MCII;
- const SourceMgr &Source;
+ llvm::ArrayRef<llvm::MCInst> Source;
llvm::MCInstPrinter &MCIP;
public:
InstructionInfoView(const llvm::MCSubtargetInfo &sti,
- const llvm::MCInstrInfo &mcii, const SourceMgr &S,
- llvm::MCInstPrinter &IP)
+ const llvm::MCInstrInfo &mcii,
+ llvm::ArrayRef<llvm::MCInst> S, llvm::MCInstPrinter &IP)
: STI(sti), MCII(mcii), Source(S), MCIP(IP) {}
void printView(llvm::raw_ostream &OS) const override;
};
} // namespace mca
+} // namespace llvm
#endif
diff --git a/tools/llvm-mca/Views/RegisterFileStatistics.cpp b/tools/llvm-mca/Views/RegisterFileStatistics.cpp
new file mode 100644
index 000000000000..06202bc41421
--- /dev/null
+++ b/tools/llvm-mca/Views/RegisterFileStatistics.cpp
@@ -0,0 +1,168 @@
+//===--------------------- RegisterFileStatistics.cpp -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the RegisterFileStatistics interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Views/RegisterFileStatistics.h"
+#include "llvm/Support/Format.h"
+
+namespace llvm {
+namespace mca {
+
+RegisterFileStatistics::RegisterFileStatistics(const MCSubtargetInfo &sti)
+ : STI(sti) {
+ const MCSchedModel &SM = STI.getSchedModel();
+ RegisterFileUsage RFUEmpty = {0, 0, 0};
+ MoveEliminationInfo MEIEmpty = {0, 0, 0, 0, 0};
+ if (!SM.hasExtraProcessorInfo()) {
+ // Assume a single register file.
+ PRFUsage.emplace_back(RFUEmpty);
+ MoveElimInfo.emplace_back(MEIEmpty);
+ return;
+ }
+
+ // Initialize a RegisterFileUsage for every user defined register file, plus
+ // the default register file which is always at index #0.
+ const MCExtraProcessorInfo &PI = SM.getExtraProcessorInfo();
+ // There is always an "InvalidRegisterFile" entry in tablegen. That entry can
+ // be skipped. If there are no user defined register files, then reserve a
+ // single entry for the default register file at index #0.
+ unsigned NumRegFiles = std::max(PI.NumRegisterFiles, 1U);
+
+ PRFUsage.resize(NumRegFiles);
+ std::fill(PRFUsage.begin(), PRFUsage.end(), RFUEmpty);
+
+ MoveElimInfo.resize(NumRegFiles);
+ std::fill(MoveElimInfo.begin(), MoveElimInfo.end(), MEIEmpty);
+}
+
+void RegisterFileStatistics::updateRegisterFileUsage(
+ ArrayRef<unsigned> UsedPhysRegs) {
+ for (unsigned I = 0, E = PRFUsage.size(); I < E; ++I) {
+ RegisterFileUsage &RFU = PRFUsage[I];
+ unsigned NumUsedPhysRegs = UsedPhysRegs[I];
+ RFU.CurrentlyUsedMappings += NumUsedPhysRegs;
+ RFU.TotalMappings += NumUsedPhysRegs;
+ RFU.MaxUsedMappings =
+ std::max(RFU.MaxUsedMappings, RFU.CurrentlyUsedMappings);
+ }
+}
+
+void RegisterFileStatistics::updateMoveElimInfo(const Instruction &Inst) {
+ if (!Inst.isOptimizableMove())
+ return;
+
+ assert(Inst.getDefs().size() == 1 && "Expected a single definition!");
+ assert(Inst.getUses().size() == 1 && "Expected a single register use!");
+ const WriteState &WS = Inst.getDefs()[0];
+ const ReadState &RS = Inst.getUses()[0];
+
+ MoveEliminationInfo &Info =
+ MoveElimInfo[Inst.getDefs()[0].getRegisterFileID()];
+ Info.TotalMoveEliminationCandidates++;
+ if (WS.isEliminated())
+ Info.CurrentMovesEliminated++;
+ if (WS.isWriteZero() && RS.isReadZero())
+ Info.TotalMovesThatPropagateZero++;
+}
+
+void RegisterFileStatistics::onEvent(const HWInstructionEvent &Event) {
+ switch (Event.Type) {
+ default:
+ break;
+ case HWInstructionEvent::Retired: {
+ const auto &RE = static_cast<const HWInstructionRetiredEvent &>(Event);
+ for (unsigned I = 0, E = PRFUsage.size(); I < E; ++I)
+ PRFUsage[I].CurrentlyUsedMappings -= RE.FreedPhysRegs[I];
+ break;
+ }
+ case HWInstructionEvent::Dispatched: {
+ const auto &DE = static_cast<const HWInstructionDispatchedEvent &>(Event);
+ updateRegisterFileUsage(DE.UsedPhysRegs);
+ updateMoveElimInfo(*DE.IR.getInstruction());
+ }
+ }
+}
+
+void RegisterFileStatistics::onCycleEnd() {
+ for (MoveEliminationInfo &MEI : MoveElimInfo) {
+ unsigned &CurrentMax = MEI.MaxMovesEliminatedPerCycle;
+ CurrentMax = std::max(CurrentMax, MEI.CurrentMovesEliminated);
+ MEI.TotalMovesEliminated += MEI.CurrentMovesEliminated;
+ MEI.CurrentMovesEliminated = 0;
+ }
+}
+
+void RegisterFileStatistics::printView(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+
+ TempStream << "\n\nRegister File statistics:";
+ const RegisterFileUsage &GlobalUsage = PRFUsage[0];
+ TempStream << "\nTotal number of mappings created: "
+ << GlobalUsage.TotalMappings;
+ TempStream << "\nMax number of mappings used: "
+ << GlobalUsage.MaxUsedMappings << '\n';
+
+ for (unsigned I = 1, E = PRFUsage.size(); I < E; ++I) {
+ const RegisterFileUsage &RFU = PRFUsage[I];
+ // Obtain the register file descriptor from the scheduling model.
+ assert(STI.getSchedModel().hasExtraProcessorInfo() &&
+ "Unable to find register file info!");
+ const MCExtraProcessorInfo &PI =
+ STI.getSchedModel().getExtraProcessorInfo();
+ assert(I <= PI.NumRegisterFiles && "Unexpected register file index!");
+ const MCRegisterFileDesc &RFDesc = PI.RegisterFiles[I];
+ // Skip invalid register files.
+ if (!RFDesc.NumPhysRegs)
+ continue;
+
+ TempStream << "\n* Register File #" << I;
+ TempStream << " -- " << StringRef(RFDesc.Name) << ':';
+ TempStream << "\n Number of physical registers: ";
+ if (!RFDesc.NumPhysRegs)
+ TempStream << "unbounded";
+ else
+ TempStream << RFDesc.NumPhysRegs;
+ TempStream << "\n Total number of mappings created: "
+ << RFU.TotalMappings;
+ TempStream << "\n Max number of mappings used: "
+ << RFU.MaxUsedMappings << '\n';
+ const MoveEliminationInfo &MEI = MoveElimInfo[I];
+
+ if (MEI.TotalMoveEliminationCandidates) {
+ TempStream << " Number of optimizable moves: "
+ << MEI.TotalMoveEliminationCandidates;
+ double EliminatedMovProportion = (double)MEI.TotalMovesEliminated /
+ MEI.TotalMoveEliminationCandidates *
+ 100.0;
+ double ZeroMovProportion = (double)MEI.TotalMovesThatPropagateZero /
+ MEI.TotalMoveEliminationCandidates * 100.0;
+ TempStream << "\n Number of moves eliminated: "
+ << MEI.TotalMovesEliminated << " "
+ << format("(%.1f%%)",
+ floor((EliminatedMovProportion * 10) + 0.5) / 10);
+ TempStream << "\n Number of zero moves: "
+ << MEI.TotalMovesThatPropagateZero << " "
+ << format("(%.1f%%)",
+ floor((ZeroMovProportion * 10) + 0.5) / 10);
+ TempStream << "\n Max moves eliminated per cycle: "
+ << MEI.MaxMovesEliminatedPerCycle << '\n';
+ }
+ }
+
+ TempStream.flush();
+ OS << Buffer;
+}
+
+} // namespace mca
+} // namespace llvm
diff --git a/tools/llvm-mca/RegisterFileStatistics.h b/tools/llvm-mca/Views/RegisterFileStatistics.h
index cbe816cd3332..a2c52a668dae 100644
--- a/tools/llvm-mca/RegisterFileStatistics.h
+++ b/tools/llvm-mca/Views/RegisterFileStatistics.h
@@ -21,6 +21,10 @@
/// Number of physical registers: 72
/// Total number of mappings created: 0
/// Max number of mappings used: 0
+/// Number of optimizable moves: 200
+/// Number of moves eliminated: 200 (100.0%)
+/// Number of zero moves: 200 (100.0%)
+/// Max moves eliminated per cycle: 2
///
/// * Register File #2 -- IntegerPRF:
/// Number of physical registers: 64
@@ -32,10 +36,11 @@
#ifndef LLVM_TOOLS_LLVM_MCA_REGISTERFILESTATISTICS_H
#define LLVM_TOOLS_LLVM_MCA_REGISTERFILESTATISTICS_H
-#include "View.h"
+#include "Views/View.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/MC/MCSubtargetInfo.h"
+namespace llvm {
namespace mca {
class RegisterFileStatistics : public View {
@@ -48,20 +53,29 @@ class RegisterFileStatistics : public View {
unsigned CurrentlyUsedMappings;
};
+ struct MoveEliminationInfo {
+ unsigned TotalMoveEliminationCandidates;
+ unsigned TotalMovesEliminated;
+ unsigned TotalMovesThatPropagateZero;
+ unsigned MaxMovesEliminatedPerCycle;
+ unsigned CurrentMovesEliminated;
+ };
+
// There is one entry for each register file implemented by the processor.
- llvm::SmallVector<RegisterFileUsage, 4> RegisterFiles;
+ llvm::SmallVector<RegisterFileUsage, 4> PRFUsage;
+ llvm::SmallVector<MoveEliminationInfo, 4> MoveElimInfo;
- void initializeRegisterFileInfo();
+ void updateRegisterFileUsage(ArrayRef<unsigned> UsedPhysRegs);
+ void updateMoveElimInfo(const Instruction &Inst);
public:
- RegisterFileStatistics(const llvm::MCSubtargetInfo &sti) : STI(sti) {
- initializeRegisterFileInfo();
- }
+ RegisterFileStatistics(const llvm::MCSubtargetInfo &sti);
+ void onCycleEnd() override;
void onEvent(const HWInstructionEvent &Event) override;
-
void printView(llvm::raw_ostream &OS) const override;
};
} // namespace mca
+} // namespace llvm
#endif
diff --git a/tools/llvm-mca/ResourcePressureView.cpp b/tools/llvm-mca/Views/ResourcePressureView.cpp
index fe9d5b7fabc8..6df61840437d 100644
--- a/tools/llvm-mca/ResourcePressureView.cpp
+++ b/tools/llvm-mca/Views/ResourcePressureView.cpp
@@ -12,15 +12,17 @@
///
//===----------------------------------------------------------------------===//
-#include "ResourcePressureView.h"
+#include "Views/ResourcePressureView.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/raw_ostream.h"
+namespace llvm {
namespace mca {
-using namespace llvm;
-
-void ResourcePressureView::initialize() {
+ResourcePressureView::ResourcePressureView(const llvm::MCSubtargetInfo &sti,
+ MCInstPrinter &Printer,
+ ArrayRef<MCInst> S)
+ : STI(sti), MCIP(Printer), Source(S), LastInstructionIdx(0) {
// Populate the map of resource descriptors.
unsigned R2VIndex = 0;
const MCSchedModel &SM = STI.getSchedModel();
@@ -41,12 +43,19 @@ void ResourcePressureView::initialize() {
}
void ResourcePressureView::onEvent(const HWInstructionEvent &Event) {
+ if (Event.Type == HWInstructionEvent::Dispatched) {
+ LastInstructionIdx = Event.IR.getSourceIndex();
+ return;
+ }
+
// We're only interested in Issue events.
if (Event.Type != HWInstructionEvent::Issued)
return;
+
const auto &IssueEvent = static_cast<const HWInstructionIssuedEvent &>(Event);
const unsigned SourceIdx = Event.IR.getSourceIndex() % Source.size();
- for (const std::pair<ResourceRef, double> &Use : IssueEvent.UsedResources) {
+ for (const std::pair<ResourceRef, ResourceCycles> &Use :
+ IssueEvent.UsedResources) {
const ResourceRef &RR = Use.first;
assert(Resource2VecIndex.find(RR.first) != Resource2VecIndex.end());
unsigned R2VIndex = Resource2VecIndex[RR.first];
@@ -91,8 +100,7 @@ static void printResourcePressure(formatted_raw_ostream &OS, double Pressure,
OS.PadToColumn(Col);
}
-void ResourcePressureView::printResourcePressurePerIteration(
- raw_ostream &OS, unsigned Executions) const {
+void ResourcePressureView::printResourcePressurePerIter(raw_ostream &OS) const {
std::string Buffer;
raw_string_ostream TempStream(Buffer);
formatted_raw_ostream FOS(TempStream);
@@ -125,6 +133,7 @@ void ResourcePressureView::printResourcePressurePerIteration(
FOS << '\n';
FOS.flush();
+ const unsigned Executions = LastInstructionIdx / Source.size() + 1;
for (unsigned I = 0, E = NumResourceUnits; I < E; ++I) {
double Usage = ResourceUsage[I + Source.size() * E];
printResourcePressure(FOS, Usage / Executions, (I + 1) * 7);
@@ -134,8 +143,7 @@ void ResourcePressureView::printResourcePressurePerIteration(
OS << Buffer;
}
-void ResourcePressureView::printResourcePressurePerInstruction(
- raw_ostream &OS, unsigned Executions) const {
+void ResourcePressureView::printResourcePressurePerInst(raw_ostream &OS) const {
std::string Buffer;
raw_string_ostream TempStream(Buffer);
formatted_raw_ostream FOS(TempStream);
@@ -147,13 +155,16 @@ void ResourcePressureView::printResourcePressurePerInstruction(
std::string Instruction;
raw_string_ostream InstrStream(Instruction);
- for (unsigned I = 0, E = Source.size(); I < E; ++I) {
+ unsigned InstrIndex = 0;
+ const unsigned Executions = LastInstructionIdx / Source.size() + 1;
+ for (const MCInst &MCI : Source) {
+ unsigned BaseEltIdx = InstrIndex * NumResourceUnits;
for (unsigned J = 0; J < NumResourceUnits; ++J) {
- double Usage = ResourceUsage[J + I * NumResourceUnits];
+ double Usage = ResourceUsage[J + BaseEltIdx];
printResourcePressure(FOS, Usage / Executions, (J + 1) * 7);
}
- MCIP.printInst(&Source.getMCInstFromIndex(I), InstrStream, "", STI);
+ MCIP.printInst(&MCI, InstrStream, "", STI);
InstrStream.flush();
StringRef Str(Instruction);
@@ -166,6 +177,9 @@ void ResourcePressureView::printResourcePressurePerInstruction(
FOS.flush();
OS << Buffer;
Buffer = "";
+
+ ++InstrIndex;
}
}
} // namespace mca
+} // namespace llvm
diff --git a/tools/llvm-mca/ResourcePressureView.h b/tools/llvm-mca/Views/ResourcePressureView.h
index fe1c6af5e6f6..572ce6fe6b70 100644
--- a/tools/llvm-mca/ResourcePressureView.h
+++ b/tools/llvm-mca/Views/ResourcePressureView.h
@@ -58,13 +58,14 @@
#ifndef LLVM_TOOLS_LLVM_MCA_RESOURCEPRESSUREVIEW_H
#define LLVM_TOOLS_LLVM_MCA_RESOURCEPRESSUREVIEW_H
-#include "SourceMgr.h"
-#include "View.h"
+#include "Views/View.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCSubtargetInfo.h"
-#include <map>
+namespace llvm {
namespace mca {
/// This class collects resource pressure statistics and it is able to print
@@ -72,38 +73,32 @@ namespace mca {
class ResourcePressureView : public View {
const llvm::MCSubtargetInfo &STI;
llvm::MCInstPrinter &MCIP;
- const SourceMgr &Source;
+ llvm::ArrayRef<llvm::MCInst> Source;
+ unsigned LastInstructionIdx;
// Map to quickly obtain the ResourceUsage column index from a processor
// resource ID.
llvm::DenseMap<unsigned, unsigned> Resource2VecIndex;
// Table of resources used by instructions.
- std::vector<double> ResourceUsage;
+ std::vector<ResourceCycles> ResourceUsage;
unsigned NumResourceUnits;
- const llvm::MCInst &GetMCInstFromIndex(unsigned Index) const;
- void printResourcePressurePerIteration(llvm::raw_ostream &OS,
- unsigned Executions) const;
- void printResourcePressurePerInstruction(llvm::raw_ostream &OS,
- unsigned Executions) const;
- void initialize();
+ void printResourcePressurePerIter(llvm::raw_ostream &OS) const;
+ void printResourcePressurePerInst(llvm::raw_ostream &OS) const;
public:
ResourcePressureView(const llvm::MCSubtargetInfo &sti,
- llvm::MCInstPrinter &Printer, const SourceMgr &SM)
- : STI(sti), MCIP(Printer), Source(SM) {
- initialize();
- }
+ llvm::MCInstPrinter &Printer,
+ llvm::ArrayRef<llvm::MCInst> S);
void onEvent(const HWInstructionEvent &Event) override;
-
void printView(llvm::raw_ostream &OS) const override {
- unsigned Executions = Source.getNumIterations();
- printResourcePressurePerIteration(OS, Executions);
- printResourcePressurePerInstruction(OS, Executions);
+ printResourcePressurePerIter(OS);
+ printResourcePressurePerInst(OS);
}
};
} // namespace mca
+} // namespace llvm
#endif
diff --git a/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp b/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp
new file mode 100644
index 000000000000..54eb28f1add9
--- /dev/null
+++ b/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp
@@ -0,0 +1,91 @@
+//===--------------------- RetireControlUnitStatistics.cpp ------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the RetireControlUnitStatistics interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Views/RetireControlUnitStatistics.h"
+#include "llvm/Support/Format.h"
+
+namespace llvm {
+namespace mca {
+
+RetireControlUnitStatistics::RetireControlUnitStatistics(const MCSchedModel &SM)
+ : NumRetired(0), NumCycles(0), EntriesInUse(0), MaxUsedEntries(0),
+ SumOfUsedEntries(0) {
+ TotalROBEntries = SM.MicroOpBufferSize;
+ if (SM.hasExtraProcessorInfo()) {
+ const MCExtraProcessorInfo &EPI = SM.getExtraProcessorInfo();
+ if (EPI.ReorderBufferSize)
+ TotalROBEntries = EPI.ReorderBufferSize;
+ }
+}
+
+void RetireControlUnitStatistics::onEvent(const HWInstructionEvent &Event) {
+ if (Event.Type == HWInstructionEvent::Dispatched) {
+ unsigned NumEntries =
+ static_cast<const HWInstructionDispatchedEvent &>(Event).MicroOpcodes;
+ EntriesInUse += NumEntries;
+ }
+
+ if (Event.Type == HWInstructionEvent::Retired) {
+ unsigned ReleasedEntries = Event.IR.getInstruction()->getDesc().NumMicroOps;
+ assert(EntriesInUse >= ReleasedEntries && "Invalid internal state!");
+ EntriesInUse -= ReleasedEntries;
+ ++NumRetired;
+ }
+}
+
+void RetireControlUnitStatistics::onCycleEnd() {
+ // Update histogram
+ RetiredPerCycle[NumRetired]++;
+ NumRetired = 0;
+ ++NumCycles;
+ MaxUsedEntries = std::max(MaxUsedEntries, EntriesInUse);
+ SumOfUsedEntries += EntriesInUse;
+}
+
+void RetireControlUnitStatistics::printView(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ TempStream << "\n\nRetire Control Unit - "
+ << "number of cycles where we saw N instructions retired:\n";
+ TempStream << "[# retired], [# cycles]\n";
+
+ for (const std::pair<unsigned, unsigned> &Entry : RetiredPerCycle) {
+ TempStream << " " << Entry.first;
+ if (Entry.first < 10)
+ TempStream << ", ";
+ else
+ TempStream << ", ";
+ TempStream << Entry.second << " ("
+ << format("%.1f", ((double)Entry.second / NumCycles) * 100.0)
+ << "%)\n";
+ }
+
+ unsigned AvgUsage = (double)SumOfUsedEntries / NumCycles;
+ double MaxUsagePercentage = ((double)MaxUsedEntries / TotalROBEntries) * 100.0;
+ double NormalizedMaxPercentage = floor((MaxUsagePercentage * 10) + 0.5) / 10;
+ double AvgUsagePercentage = ((double)AvgUsage / TotalROBEntries) * 100.0;
+ double NormalizedAvgPercentage = floor((AvgUsagePercentage * 10) + 0.5) / 10;
+
+ TempStream << "\nTotal ROB Entries: " << TotalROBEntries
+ << "\nMax Used ROB Entries: " << MaxUsedEntries
+ << format(" ( %.1f%% )", NormalizedMaxPercentage)
+ << "\nAverage Used ROB Entries per cy: " << AvgUsage
+ << format(" ( %.1f%% )\n", NormalizedAvgPercentage);
+
+ TempStream.flush();
+ OS << Buffer;
+}
+
+} // namespace mca
+} // namespace llvm
diff --git a/tools/llvm-mca/RetireControlUnitStatistics.h b/tools/llvm-mca/Views/RetireControlUnitStatistics.h
index 1f03e7efe889..02aa13bc444a 100644
--- a/tools/llvm-mca/RetireControlUnitStatistics.h
+++ b/tools/llvm-mca/Views/RetireControlUnitStatistics.h
@@ -16,20 +16,24 @@
///
/// Retire Control Unit - number of cycles where we saw N instructions retired:
/// [# retired], [# cycles]
-/// 0, 9 (6.9%)
-/// 1, 6 (4.6%)
-/// 2, 1 (0.8%)
-/// 4, 3 (2.3%)
+/// 0, 109 (17.9%)
+/// 1, 102 (16.7%)
+/// 2, 399 (65.4%)
+///
+/// Total ROB Entries: 64
+/// Max Used ROB Entries: 35 ( 54.7% )
+/// Average Used ROB Entries per cy: 32 ( 50.0% )
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_MCA_RETIRECONTROLUNITSTATISTICS_H
#define LLVM_TOOLS_LLVM_MCA_RETIRECONTROLUNITSTATISTICS_H
-#include "View.h"
-#include "llvm/MC/MCSubtargetInfo.h"
+#include "Views/View.h"
+#include "llvm/MC/MCSchedule.h"
#include <map>
+namespace llvm {
namespace mca {
class RetireControlUnitStatistics : public View {
@@ -38,23 +42,20 @@ class RetireControlUnitStatistics : public View {
unsigned NumRetired;
unsigned NumCycles;
-
- void updateHistograms() {
- RetiredPerCycle[NumRetired]++;
- NumRetired = 0;
- }
+ unsigned TotalROBEntries;
+ unsigned EntriesInUse;
+ unsigned MaxUsedEntries;
+ unsigned SumOfUsedEntries;
public:
- RetireControlUnitStatistics() : NumRetired(0), NumCycles(0) {}
+ RetireControlUnitStatistics(const MCSchedModel &SM);
void onEvent(const HWInstructionEvent &Event) override;
-
- void onCycleBegin() override { NumCycles++; }
-
- void onCycleEnd() override { updateHistograms(); }
-
+ void onCycleEnd() override;
void printView(llvm::raw_ostream &OS) const override;
};
+
} // namespace mca
+} // namespace llvm
#endif
diff --git a/tools/llvm-mca/Views/SchedulerStatistics.cpp b/tools/llvm-mca/Views/SchedulerStatistics.cpp
new file mode 100644
index 000000000000..670f90127f18
--- /dev/null
+++ b/tools/llvm-mca/Views/SchedulerStatistics.cpp
@@ -0,0 +1,183 @@
+//===--------------------- SchedulerStatistics.cpp --------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the SchedulerStatistics interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Views/SchedulerStatistics.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FormattedStream.h"
+
+namespace llvm {
+namespace mca {
+
+SchedulerStatistics::SchedulerStatistics(const llvm::MCSubtargetInfo &STI)
+ : SM(STI.getSchedModel()), LQResourceID(0), SQResourceID(0), NumIssued(0),
+ NumCycles(0), MostRecentLoadDispatched(~0U),
+ MostRecentStoreDispatched(~0U),
+ IssuedPerCycle(STI.getSchedModel().NumProcResourceKinds, 0),
+ Usage(STI.getSchedModel().NumProcResourceKinds, {0, 0, 0}) {
+ if (SM.hasExtraProcessorInfo()) {
+ const MCExtraProcessorInfo &EPI = SM.getExtraProcessorInfo();
+ LQResourceID = EPI.LoadQueueID;
+ SQResourceID = EPI.StoreQueueID;
+ }
+}
+
+// FIXME: This implementation works under the assumption that load/store queue
+// entries are reserved at 'instruction dispatched' stage, and released at
+// 'instruction executed' stage. This currently matches the behavior of LSUnit.
+//
+// The current design minimizes the number of events generated by the
+// Dispatch/Execute stages, at the cost of doing extra bookkeeping in method
+// `onEvent`. However, it introduces a subtle dependency between this view and
+// how the LSUnit works.
+//
+// In future we should add a new "memory queue" event type, so that we stop
+// making assumptions on how LSUnit internally works (See PR39828).
+void SchedulerStatistics::onEvent(const HWInstructionEvent &Event) {
+ if (Event.Type == HWInstructionEvent::Issued)
+ ++NumIssued;
+ else if (Event.Type == HWInstructionEvent::Dispatched) {
+ const Instruction &Inst = *Event.IR.getInstruction();
+ const unsigned Index = Event.IR.getSourceIndex();
+ if (LQResourceID && Inst.getDesc().MayLoad &&
+ MostRecentLoadDispatched != Index) {
+ Usage[LQResourceID].SlotsInUse++;
+ MostRecentLoadDispatched = Index;
+ }
+ if (SQResourceID && Inst.getDesc().MayStore &&
+ MostRecentStoreDispatched != Index) {
+ Usage[SQResourceID].SlotsInUse++;
+ MostRecentStoreDispatched = Index;
+ }
+ } else if (Event.Type == HWInstructionEvent::Executed) {
+ const Instruction &Inst = *Event.IR.getInstruction();
+ if (LQResourceID && Inst.getDesc().MayLoad) {
+ assert(Usage[LQResourceID].SlotsInUse);
+ Usage[LQResourceID].SlotsInUse--;
+ }
+ if (SQResourceID && Inst.getDesc().MayStore) {
+ assert(Usage[SQResourceID].SlotsInUse);
+ Usage[SQResourceID].SlotsInUse--;
+ }
+ }
+}
+
+void SchedulerStatistics::onReservedBuffers(const InstRef & /* unused */,
+ ArrayRef<unsigned> Buffers) {
+ for (const unsigned Buffer : Buffers) {
+ if (Buffer == LQResourceID || Buffer == SQResourceID)
+ continue;
+ Usage[Buffer].SlotsInUse++;
+ }
+}
+
+void SchedulerStatistics::onReleasedBuffers(const InstRef & /* unused */,
+ ArrayRef<unsigned> Buffers) {
+ for (const unsigned Buffer : Buffers) {
+ if (Buffer == LQResourceID || Buffer == SQResourceID)
+ continue;
+ Usage[Buffer].SlotsInUse--;
+ }
+}
+
+void SchedulerStatistics::updateHistograms() {
+ for (BufferUsage &BU : Usage) {
+ BU.CumulativeNumUsedSlots += BU.SlotsInUse;
+ BU.MaxUsedSlots = std::max(BU.MaxUsedSlots, BU.SlotsInUse);
+ }
+
+ IssuedPerCycle[NumIssued]++;
+ NumIssued = 0;
+}
+
+void SchedulerStatistics::printSchedulerStats(raw_ostream &OS) const {
+ OS << "\n\nSchedulers - "
+ << "number of cycles where we saw N instructions issued:\n";
+ OS << "[# issued], [# cycles]\n";
+
+ const auto It =
+ std::max_element(IssuedPerCycle.begin(), IssuedPerCycle.end());
+ unsigned Index = std::distance(IssuedPerCycle.begin(), It);
+
+ bool HasColors = OS.has_colors();
+ for (unsigned I = 0, E = IssuedPerCycle.size(); I < E; ++I) {
+ unsigned IPC = IssuedPerCycle[I];
+ if (!IPC)
+ continue;
+
+ if (I == Index && HasColors)
+ OS.changeColor(raw_ostream::SAVEDCOLOR, true, false);
+
+ OS << " " << I << ", " << IPC << " ("
+ << format("%.1f", ((double)IPC / NumCycles) * 100) << "%)\n";
+ if (HasColors)
+ OS.resetColor();
+ }
+}
+
+void SchedulerStatistics::printSchedulerUsage(raw_ostream &OS) const {
+ assert(NumCycles && "Unexpected number of cycles!");
+
+ OS << "\nScheduler's queue usage:\n";
+ if (all_of(Usage, [](const BufferUsage &BU) { return !BU.MaxUsedSlots; })) {
+ OS << "No scheduler resources used.\n";
+ return;
+ }
+
+ OS << "[1] Resource name.\n"
+ << "[2] Average number of used buffer entries.\n"
+ << "[3] Maximum number of used buffer entries.\n"
+ << "[4] Total number of buffer entries.\n\n"
+ << " [1] [2] [3] [4]\n";
+
+ formatted_raw_ostream FOS(OS);
+ bool HasColors = FOS.has_colors();
+ for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
+ const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
+ if (ProcResource.BufferSize <= 0)
+ continue;
+
+ const BufferUsage &BU = Usage[I];
+ double AvgUsage = (double)BU.CumulativeNumUsedSlots / NumCycles;
+ double AlmostFullThreshold = (double)(ProcResource.BufferSize * 4) / 5;
+ unsigned NormalizedAvg = floor((AvgUsage * 10) + 0.5) / 10;
+ unsigned NormalizedThreshold = floor((AlmostFullThreshold * 10) + 0.5) / 10;
+
+ FOS << ProcResource.Name;
+ FOS.PadToColumn(17);
+ if (HasColors && NormalizedAvg >= NormalizedThreshold)
+ FOS.changeColor(raw_ostream::YELLOW, true, false);
+ FOS << NormalizedAvg;
+ if (HasColors)
+ FOS.resetColor();
+ FOS.PadToColumn(28);
+ if (HasColors &&
+ BU.MaxUsedSlots == static_cast<unsigned>(ProcResource.BufferSize))
+ FOS.changeColor(raw_ostream::RED, true, false);
+ FOS << BU.MaxUsedSlots;
+ if (HasColors)
+ FOS.resetColor();
+ FOS.PadToColumn(39);
+ FOS << ProcResource.BufferSize << '\n';
+ }
+
+ FOS.flush();
+}
+
+void SchedulerStatistics::printView(raw_ostream &OS) const {
+ printSchedulerStats(OS);
+ printSchedulerUsage(OS);
+}
+
+} // namespace mca
+} // namespace llvm
diff --git a/tools/llvm-mca/SchedulerStatistics.h b/tools/llvm-mca/Views/SchedulerStatistics.h
index 7383c54a1615..d99a395a726d 100644
--- a/tools/llvm-mca/SchedulerStatistics.h
+++ b/tools/llvm-mca/Views/SchedulerStatistics.h
@@ -17,75 +17,78 @@
///
/// Schedulers - number of cycles where we saw N instructions issued:
/// [# issued], [# cycles]
-/// 0, 7 (5.4%)
-/// 1, 4 (3.1%)
-/// 2, 8 (6.2%)
+/// 0, 6 (2.9%)
+/// 1, 106 (50.7%)
+/// 2, 97 (46.4%)
///
/// Scheduler's queue usage:
-/// JALU01, 0/20
-/// JFPU01, 18/18
-/// JLSAGU, 0/12
+/// [1] Resource name.
+/// [2] Average number of used buffer entries.
+/// [3] Maximum number of used buffer entries.
+/// [4] Total number of buffer entries.
///
+/// [1] [2] [3] [4]
+/// JALU01 0 0 20
+/// JFPU01 15 18 18
+/// JLSAGU 0 0 12
+//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_MCA_SCHEDULERSTATISTICS_H
#define LLVM_TOOLS_LLVM_MCA_SCHEDULERSTATISTICS_H
-#include "View.h"
+#include "Views/View.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include <map>
+namespace llvm {
namespace mca {
-class SchedulerStatistics : public View {
+class SchedulerStatistics final : public View {
const llvm::MCSchedModel &SM;
-
- using Histogram = std::map<unsigned, unsigned>;
- Histogram IssuedPerCycle;
+ unsigned LQResourceID;
+ unsigned SQResourceID;
unsigned NumIssued;
unsigned NumCycles;
+ unsigned MostRecentLoadDispatched;
+ unsigned MostRecentStoreDispatched;
+
// Tracks the usage of a scheduler's queue.
struct BufferUsage {
unsigned SlotsInUse;
unsigned MaxUsedSlots;
+ uint64_t CumulativeNumUsedSlots;
};
- std::map<unsigned, BufferUsage> BufferedResources;
-
- void updateHistograms() {
- IssuedPerCycle[NumIssued]++;
- NumIssued = 0;
- }
+ std::vector<unsigned> IssuedPerCycle;
+ std::vector<BufferUsage> Usage;
- void printSchedulerStatistics(llvm::raw_ostream &OS) const;
+ void updateHistograms();
+ void printSchedulerStats(llvm::raw_ostream &OS) const;
void printSchedulerUsage(llvm::raw_ostream &OS) const;
public:
- SchedulerStatistics(const llvm::MCSubtargetInfo &STI)
- : SM(STI.getSchedModel()), NumIssued(0), NumCycles(0) {}
-
+ SchedulerStatistics(const llvm::MCSubtargetInfo &STI);
void onEvent(const HWInstructionEvent &Event) override;
-
void onCycleBegin() override { NumCycles++; }
-
void onCycleEnd() override { updateHistograms(); }
// Increases the number of used scheduler queue slots of every buffered
// resource in the Buffers set.
- void onReservedBuffers(llvm::ArrayRef<unsigned> Buffers) override;
+ void onReservedBuffers(const InstRef &IR,
+ llvm::ArrayRef<unsigned> Buffers) override;
// Decreases by one the number of used scheduler queue slots of every
// buffered resource in the Buffers set.
- void onReleasedBuffers(llvm::ArrayRef<unsigned> Buffers) override;
+ void onReleasedBuffers(const InstRef &IR,
+ llvm::ArrayRef<unsigned> Buffers) override;
- void printView(llvm::raw_ostream &OS) const override {
- printSchedulerStatistics(OS);
- printSchedulerUsage(OS);
- }
+ void printView(llvm::raw_ostream &OS) const override;
};
} // namespace mca
+} // namespace llvm
#endif
diff --git a/tools/llvm-mca/SummaryView.cpp b/tools/llvm-mca/Views/SummaryView.cpp
index 01399055c4fd..d8ac709e784d 100644
--- a/tools/llvm-mca/SummaryView.cpp
+++ b/tools/llvm-mca/Views/SummaryView.cpp
@@ -13,32 +13,33 @@
///
//===----------------------------------------------------------------------===//
-#include "SummaryView.h"
-#include "Support.h"
+#include "Views/SummaryView.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/MCA/Support.h"
#include "llvm/Support/Format.h"
+namespace llvm {
namespace mca {
#define DEBUG_TYPE "llvm-mca"
-using namespace llvm;
-
-SummaryView::SummaryView(const llvm::MCSchedModel &Model, const SourceMgr &S,
+SummaryView::SummaryView(const MCSchedModel &Model, ArrayRef<MCInst> S,
unsigned Width)
- : SM(Model), Source(S), DispatchWidth(Width), TotalCycles(0),
- NumMicroOps(0), ProcResourceUsage(Model.getNumProcResourceKinds(), 0),
- ProcResourceMasks(Model.getNumProcResourceKinds(), 0) {
+ : SM(Model), Source(S), DispatchWidth(Width), LastInstructionIdx(0),
+ TotalCycles(0), NumMicroOps(0),
+ ProcResourceUsage(Model.getNumProcResourceKinds(), 0),
+ ProcResourceMasks(Model.getNumProcResourceKinds()) {
computeProcResourceMasks(SM, ProcResourceMasks);
}
void SummaryView::onEvent(const HWInstructionEvent &Event) {
- // We are only interested in the "instruction dispatched" events generated by
- // the dispatch stage for instructions that are part of iteration #0.
- if (Event.Type != HWInstructionEvent::Dispatched)
- return;
+ if (Event.Type == HWInstructionEvent::Dispatched)
+ LastInstructionIdx = Event.IR.getSourceIndex();
- if (Event.IR.getSourceIndex() >= Source.size())
+ // We are only interested in the "instruction retired" events generated by
+ // the retire stage for instructions that are part of iteration #0.
+ if (Event.Type != HWInstructionEvent::Retired ||
+ Event.IR.getSourceIndex() >= Source.size())
return;
// Update the cumulative number of resource cycles based on the processor
@@ -60,10 +61,12 @@ void SummaryView::onEvent(const HWInstructionEvent &Event) {
}
void SummaryView::printView(raw_ostream &OS) const {
- unsigned Iterations = Source.getNumIterations();
unsigned Instructions = Source.size();
+ unsigned Iterations = (LastInstructionIdx / Instructions) + 1;
unsigned TotalInstructions = Instructions * Iterations;
+ unsigned TotalUOps = NumMicroOps * Iterations;
double IPC = (double)TotalInstructions / TotalCycles;
+ double UOpsPerCycle = (double)TotalUOps / TotalCycles;
double BlockRThroughput = computeBlockRThroughput(
SM, DispatchWidth, NumMicroOps, ProcResourceUsage);
@@ -72,10 +75,12 @@ void SummaryView::printView(raw_ostream &OS) const {
TempStream << "Iterations: " << Iterations;
TempStream << "\nInstructions: " << TotalInstructions;
TempStream << "\nTotal Cycles: " << TotalCycles;
+ TempStream << "\nTotal uOps: " << TotalUOps << '\n';
TempStream << "\nDispatch Width: " << DispatchWidth;
- TempStream << "\nIPC: " << format("%.2f", IPC);
-
- // Round to the block reciprocal throughput to the nearest tenth.
+ TempStream << "\nuOps Per Cycle: "
+ << format("%.2f", floor((UOpsPerCycle * 100) + 0.5) / 100);
+ TempStream << "\nIPC: "
+ << format("%.2f", floor((IPC * 100) + 0.5) / 100);
TempStream << "\nBlock RThroughput: "
<< format("%.1f", floor((BlockRThroughput * 10) + 0.5) / 10)
<< '\n';
@@ -83,3 +88,4 @@ void SummaryView::printView(raw_ostream &OS) const {
OS << Buffer;
}
} // namespace mca.
+} // namespace llvm
diff --git a/tools/llvm-mca/SummaryView.h b/tools/llvm-mca/Views/SummaryView.h
index b799ce3aa747..f59fd4233fbe 100644
--- a/tools/llvm-mca/SummaryView.h
+++ b/tools/llvm-mca/Views/SummaryView.h
@@ -29,19 +29,20 @@
#ifndef LLVM_TOOLS_LLVM_MCA_SUMMARYVIEW_H
#define LLVM_TOOLS_LLVM_MCA_SUMMARYVIEW_H
-#include "SourceMgr.h"
-#include "View.h"
+#include "Views/View.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/MC/MCSchedule.h"
#include "llvm/Support/raw_ostream.h"
+namespace llvm {
namespace mca {
/// A view that collects and prints a few performance numbers.
class SummaryView : public View {
const llvm::MCSchedModel &SM;
- const SourceMgr &Source;
+ llvm::ArrayRef<llvm::MCInst> Source;
const unsigned DispatchWidth;
+ unsigned LastInstructionIdx;
unsigned TotalCycles;
// The total number of micro opcodes contributed by a block of instructions.
unsigned NumMicroOps;
@@ -62,15 +63,15 @@ class SummaryView : public View {
double getBlockRThroughput() const;
public:
- SummaryView(const llvm::MCSchedModel &Model, const SourceMgr &S,
+ SummaryView(const llvm::MCSchedModel &Model, llvm::ArrayRef<llvm::MCInst> S,
unsigned Width);
void onCycleEnd() override { ++TotalCycles; }
-
void onEvent(const HWInstructionEvent &Event) override;
void printView(llvm::raw_ostream &OS) const override;
};
} // namespace mca
+} // namespace llvm
#endif
diff --git a/tools/llvm-mca/Views/TimelineView.cpp b/tools/llvm-mca/Views/TimelineView.cpp
new file mode 100644
index 000000000000..7d55bbc99c73
--- /dev/null
+++ b/tools/llvm-mca/Views/TimelineView.cpp
@@ -0,0 +1,294 @@
+//===--------------------- TimelineView.cpp ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \brief
+///
+/// This file implements the TimelineView interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Views/TimelineView.h"
+
+namespace llvm {
+namespace mca {
+
+TimelineView::TimelineView(const MCSubtargetInfo &sti, MCInstPrinter &Printer,
+ llvm::ArrayRef<llvm::MCInst> S, unsigned Iterations,
+ unsigned Cycles)
+ : STI(sti), MCIP(Printer), Source(S), CurrentCycle(0),
+ MaxCycle(Cycles == 0 ? 80 : Cycles), LastCycle(0), WaitTime(S.size()),
+ UsedBuffer(S.size()) {
+ unsigned NumInstructions = Source.size();
+ assert(Iterations && "Invalid number of iterations specified!");
+ NumInstructions *= Iterations;
+ Timeline.resize(NumInstructions);
+ TimelineViewEntry InvalidTVEntry = {-1, 0, 0, 0, 0};
+ std::fill(Timeline.begin(), Timeline.end(), InvalidTVEntry);
+
+ WaitTimeEntry NullWTEntry = {0, 0, 0};
+ std::fill(WaitTime.begin(), WaitTime.end(), NullWTEntry);
+
+ std::pair<unsigned, int> NullUsedBufferEntry = {/* Invalid resource ID*/ 0,
+ /* unknown buffer size */ -1};
+ std::fill(UsedBuffer.begin(), UsedBuffer.end(), NullUsedBufferEntry);
+}
+
+void TimelineView::onReservedBuffers(const InstRef &IR,
+ ArrayRef<unsigned> Buffers) {
+ if (IR.getSourceIndex() >= Source.size())
+ return;
+
+ const MCSchedModel &SM = STI.getSchedModel();
+ std::pair<unsigned, int> BufferInfo = {0, -1};
+ for (const unsigned Buffer : Buffers) {
+ const MCProcResourceDesc &MCDesc = *SM.getProcResource(Buffer);
+ if (!BufferInfo.first || BufferInfo.second > MCDesc.BufferSize) {
+ BufferInfo.first = Buffer;
+ BufferInfo.second = MCDesc.BufferSize;
+ }
+ }
+
+ UsedBuffer[IR.getSourceIndex()] = BufferInfo;
+}
+
+void TimelineView::onEvent(const HWInstructionEvent &Event) {
+ const unsigned Index = Event.IR.getSourceIndex();
+ if (Index >= Timeline.size())
+ return;
+
+ switch (Event.Type) {
+ case HWInstructionEvent::Retired: {
+ TimelineViewEntry &TVEntry = Timeline[Index];
+ if (CurrentCycle < MaxCycle)
+ TVEntry.CycleRetired = CurrentCycle;
+
+ // Update the WaitTime entry which corresponds to this Index.
+ assert(TVEntry.CycleDispatched >= 0 && "Invalid TVEntry found!");
+ unsigned CycleDispatched = static_cast<unsigned>(TVEntry.CycleDispatched);
+ WaitTimeEntry &WTEntry = WaitTime[Index % Source.size()];
+ WTEntry.CyclesSpentInSchedulerQueue +=
+ TVEntry.CycleIssued - CycleDispatched;
+ assert(CycleDispatched <= TVEntry.CycleReady &&
+ "Instruction cannot be ready if it hasn't been dispatched yet!");
+ WTEntry.CyclesSpentInSQWhileReady +=
+ TVEntry.CycleIssued - TVEntry.CycleReady;
+ WTEntry.CyclesSpentAfterWBAndBeforeRetire +=
+ (CurrentCycle - 1) - TVEntry.CycleExecuted;
+ break;
+ }
+ case HWInstructionEvent::Ready:
+ Timeline[Index].CycleReady = CurrentCycle;
+ break;
+ case HWInstructionEvent::Issued:
+ Timeline[Index].CycleIssued = CurrentCycle;
+ break;
+ case HWInstructionEvent::Executed:
+ Timeline[Index].CycleExecuted = CurrentCycle;
+ break;
+ case HWInstructionEvent::Dispatched:
+ // There may be multiple dispatch events. Microcoded instructions that are
+ // expanded into multiple uOps may require multiple dispatch cycles. Here,
+ // we want to capture the first dispatch cycle.
+ if (Timeline[Index].CycleDispatched == -1)
+ Timeline[Index].CycleDispatched = static_cast<int>(CurrentCycle);
+ break;
+ default:
+ return;
+ }
+ if (CurrentCycle < MaxCycle)
+ LastCycle = std::max(LastCycle, CurrentCycle);
+}
+
+static raw_ostream::Colors chooseColor(unsigned CumulativeCycles,
+ unsigned Executions, int BufferSize) {
+ if (CumulativeCycles && BufferSize < 0)
+ return raw_ostream::MAGENTA;
+ unsigned Size = static_cast<unsigned>(BufferSize);
+ if (CumulativeCycles >= Size * Executions)
+ return raw_ostream::RED;
+ if ((CumulativeCycles * 2) >= Size * Executions)
+ return raw_ostream::YELLOW;
+ return raw_ostream::SAVEDCOLOR;
+}
+
+static void tryChangeColor(raw_ostream &OS, unsigned Cycles,
+ unsigned Executions, int BufferSize) {
+ if (!OS.has_colors())
+ return;
+
+ raw_ostream::Colors Color = chooseColor(Cycles, Executions, BufferSize);
+ if (Color == raw_ostream::SAVEDCOLOR) {
+ OS.resetColor();
+ return;
+ }
+ OS.changeColor(Color, /* bold */ true, /* BG */ false);
+}
+
+void TimelineView::printWaitTimeEntry(formatted_raw_ostream &OS,
+ const WaitTimeEntry &Entry,
+ unsigned SourceIndex,
+ unsigned Executions) const {
+ OS << SourceIndex << '.';
+ OS.PadToColumn(7);
+
+ double AverageTime1, AverageTime2, AverageTime3;
+ AverageTime1 = (double)Entry.CyclesSpentInSchedulerQueue / Executions;
+ AverageTime2 = (double)Entry.CyclesSpentInSQWhileReady / Executions;
+ AverageTime3 = (double)Entry.CyclesSpentAfterWBAndBeforeRetire / Executions;
+
+ OS << Executions;
+ OS.PadToColumn(13);
+ int BufferSize = UsedBuffer[SourceIndex].second;
+ tryChangeColor(OS, Entry.CyclesSpentInSchedulerQueue, Executions, BufferSize);
+ OS << format("%.1f", floor((AverageTime1 * 10) + 0.5) / 10);
+ OS.PadToColumn(20);
+ tryChangeColor(OS, Entry.CyclesSpentInSQWhileReady, Executions, BufferSize);
+ OS << format("%.1f", floor((AverageTime2 * 10) + 0.5) / 10);
+ OS.PadToColumn(27);
+ tryChangeColor(OS, Entry.CyclesSpentAfterWBAndBeforeRetire, Executions,
+ STI.getSchedModel().MicroOpBufferSize);
+ OS << format("%.1f", floor((AverageTime3 * 10) + 0.5) / 10);
+
+ if (OS.has_colors())
+ OS.resetColor();
+ OS.PadToColumn(34);
+}
+
+void TimelineView::printAverageWaitTimes(raw_ostream &OS) const {
+ std::string Header =
+ "\n\nAverage Wait times (based on the timeline view):\n"
+ "[0]: Executions\n"
+ "[1]: Average time spent waiting in a scheduler's queue\n"
+ "[2]: Average time spent waiting in a scheduler's queue while ready\n"
+ "[3]: Average time elapsed from WB until retire stage\n\n"
+ " [0] [1] [2] [3]\n";
+ OS << Header;
+
+ // Use a different string stream for printing instructions.
+ std::string Instruction;
+ raw_string_ostream InstrStream(Instruction);
+
+ formatted_raw_ostream FOS(OS);
+ unsigned Executions = Timeline.size() / Source.size();
+ unsigned IID = 0;
+ for (const MCInst &Inst : Source) {
+ printWaitTimeEntry(FOS, WaitTime[IID], IID, Executions);
+ // Append the instruction info at the end of the line.
+ MCIP.printInst(&Inst, InstrStream, "", STI);
+ InstrStream.flush();
+
+ // Consume any tabs or spaces at the beginning of the string.
+ StringRef Str(Instruction);
+ Str = Str.ltrim();
+ FOS << " " << Str << '\n';
+ FOS.flush();
+ Instruction = "";
+
+ ++IID;
+ }
+}
+
+void TimelineView::printTimelineViewEntry(formatted_raw_ostream &OS,
+ const TimelineViewEntry &Entry,
+ unsigned Iteration,
+ unsigned SourceIndex) const {
+ if (Iteration == 0 && SourceIndex == 0)
+ OS << '\n';
+ OS << '[' << Iteration << ',' << SourceIndex << ']';
+ OS.PadToColumn(10);
+ assert(Entry.CycleDispatched >= 0 && "Invalid TimelineViewEntry!");
+ unsigned CycleDispatched = static_cast<unsigned>(Entry.CycleDispatched);
+ for (unsigned I = 0, E = CycleDispatched; I < E; ++I)
+ OS << ((I % 5 == 0) ? '.' : ' ');
+ OS << TimelineView::DisplayChar::Dispatched;
+ if (CycleDispatched != Entry.CycleExecuted) {
+ // Zero latency instructions have the same value for CycleDispatched,
+ // CycleIssued and CycleExecuted.
+ for (unsigned I = CycleDispatched + 1, E = Entry.CycleIssued; I < E; ++I)
+ OS << TimelineView::DisplayChar::Waiting;
+ if (Entry.CycleIssued == Entry.CycleExecuted)
+ OS << TimelineView::DisplayChar::DisplayChar::Executed;
+ else {
+ if (CycleDispatched != Entry.CycleIssued)
+ OS << TimelineView::DisplayChar::Executing;
+ for (unsigned I = Entry.CycleIssued + 1, E = Entry.CycleExecuted; I < E;
+ ++I)
+ OS << TimelineView::DisplayChar::Executing;
+ OS << TimelineView::DisplayChar::Executed;
+ }
+ }
+
+ for (unsigned I = Entry.CycleExecuted + 1, E = Entry.CycleRetired; I < E; ++I)
+ OS << TimelineView::DisplayChar::RetireLag;
+ OS << TimelineView::DisplayChar::Retired;
+
+ // Skip other columns.
+ for (unsigned I = Entry.CycleRetired + 1, E = LastCycle; I <= E; ++I)
+ OS << ((I % 5 == 0 || I == LastCycle) ? '.' : ' ');
+}
+
+static void printTimelineHeader(formatted_raw_ostream &OS, unsigned Cycles) {
+ OS << "\n\nTimeline view:\n";
+ if (Cycles >= 10) {
+ OS.PadToColumn(10);
+ for (unsigned I = 0; I <= Cycles; ++I) {
+ if (((I / 10) & 1) == 0)
+ OS << ' ';
+ else
+ OS << I % 10;
+ }
+ OS << '\n';
+ }
+
+ OS << "Index";
+ OS.PadToColumn(10);
+ for (unsigned I = 0; I <= Cycles; ++I) {
+ if (((I / 10) & 1) == 0)
+ OS << I % 10;
+ else
+ OS << ' ';
+ }
+ OS << '\n';
+}
+
+void TimelineView::printTimeline(raw_ostream &OS) const {
+ formatted_raw_ostream FOS(OS);
+ printTimelineHeader(FOS, LastCycle);
+ FOS.flush();
+
+ // Use a different string stream for the instruction.
+ std::string Instruction;
+ raw_string_ostream InstrStream(Instruction);
+
+ unsigned IID = 0;
+ const unsigned Iterations = Timeline.size() / Source.size();
+ for (unsigned Iteration = 0; Iteration < Iterations; ++Iteration) {
+ for (const MCInst &Inst : Source) {
+ const TimelineViewEntry &Entry = Timeline[IID];
+ if (Entry.CycleRetired == 0)
+ return;
+
+ unsigned SourceIndex = IID % Source.size();
+ printTimelineViewEntry(FOS, Entry, Iteration, SourceIndex);
+ // Append the instruction info at the end of the line.
+ MCIP.printInst(&Inst, InstrStream, "", STI);
+ InstrStream.flush();
+
+ // Consume any tabs or spaces at the beginning of the string.
+ StringRef Str(Instruction);
+ Str = Str.ltrim();
+ FOS << " " << Str << '\n';
+ FOS.flush();
+ Instruction = "";
+
+ ++IID;
+ }
+ }
+}
+} // namespace mca
+} // namespace llvm
diff --git a/tools/llvm-mca/TimelineView.h b/tools/llvm-mca/Views/TimelineView.h
index e53c23ec1cc2..ee981800161c 100644
--- a/tools/llvm-mca/TimelineView.h
+++ b/tools/llvm-mca/Views/TimelineView.h
@@ -100,14 +100,15 @@
#ifndef LLVM_TOOLS_LLVM_MCA_TIMELINEVIEW_H
#define LLVM_TOOLS_LLVM_MCA_TIMELINEVIEW_H
-#include "SourceMgr.h"
-#include "View.h"
+#include "Views/View.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/raw_ostream.h"
-#include <map>
+namespace llvm {
namespace mca {
/// This class listens to instruction state transition events
@@ -120,14 +121,14 @@ namespace mca {
class TimelineView : public View {
const llvm::MCSubtargetInfo &STI;
llvm::MCInstPrinter &MCIP;
- const SourceMgr &AsmSequence;
+ llvm::ArrayRef<llvm::MCInst> Source;
unsigned CurrentCycle;
unsigned MaxCycle;
unsigned LastCycle;
struct TimelineViewEntry {
- unsigned CycleDispatched;
+ int CycleDispatched; // A negative value is an "invalid cycle".
unsigned CycleReady;
unsigned CycleIssued;
unsigned CycleExecuted;
@@ -136,22 +137,22 @@ class TimelineView : public View {
std::vector<TimelineViewEntry> Timeline;
struct WaitTimeEntry {
- unsigned Executions;
unsigned CyclesSpentInSchedulerQueue;
unsigned CyclesSpentInSQWhileReady;
unsigned CyclesSpentAfterWBAndBeforeRetire;
};
std::vector<WaitTimeEntry> WaitTime;
+ // This field is used to map instructions to buffered resources.
+ // Elements of this vector are <resourceID, BufferSizer> pairs.
+ std::vector<std::pair<unsigned, int>> UsedBuffer;
+
void printTimelineViewEntry(llvm::formatted_raw_ostream &OS,
const TimelineViewEntry &E, unsigned Iteration,
unsigned SourceIndex) const;
void printWaitTimeEntry(llvm::formatted_raw_ostream &OS,
- const WaitTimeEntry &E, unsigned Index) const;
-
- const unsigned DEFAULT_ITERATIONS = 10;
-
- void initialize(unsigned MaxIterations);
+ const WaitTimeEntry &E, unsigned Index,
+ unsigned Executions) const;
// Display characters for the TimelineView report output.
struct DisplayChar {
@@ -165,16 +166,14 @@ class TimelineView : public View {
public:
TimelineView(const llvm::MCSubtargetInfo &sti, llvm::MCInstPrinter &Printer,
- const SourceMgr &Sequence, unsigned MaxIterations,
- unsigned Cycles)
- : STI(sti), MCIP(Printer), AsmSequence(Sequence), CurrentCycle(0),
- MaxCycle(Cycles == 0 ? 80 : Cycles), LastCycle(0) {
- initialize(MaxIterations);
- }
+ llvm::ArrayRef<llvm::MCInst> S, unsigned Iterations,
+ unsigned Cycles);
// Event handlers.
void onCycleEnd() override { ++CurrentCycle; }
void onEvent(const HWInstructionEvent &Event) override;
+ void onReservedBuffers(const InstRef &IR,
+ llvm::ArrayRef<unsigned> Buffers) override;
// print functionalities.
void printTimeline(llvm::raw_ostream &OS) const;
@@ -185,5 +184,6 @@ public:
}
};
} // namespace mca
+} // namespace llvm
#endif
diff --git a/tools/llvm-mca/View.cpp b/tools/llvm-mca/Views/View.cpp
index 390a7aeb3b9d..6cfb9dd9f394 100644
--- a/tools/llvm-mca/View.cpp
+++ b/tools/llvm-mca/Views/View.cpp
@@ -12,9 +12,11 @@
///
//===----------------------------------------------------------------------===//
-#include "View.h"
+#include "Views/View.h"
+namespace llvm {
namespace mca {
void View::anchor() {}
} // namespace mca
+} // namespace llvm
diff --git a/tools/llvm-mca/View.h b/tools/llvm-mca/Views/View.h
index 9ba94a5da977..4b82b0da0d27 100644
--- a/tools/llvm-mca/View.h
+++ b/tools/llvm-mca/Views/View.h
@@ -16,9 +16,10 @@
#ifndef LLVM_TOOLS_LLVM_MCA_VIEW_H
#define LLVM_TOOLS_LLVM_MCA_VIEW_H
-#include "HWEventListener.h"
+#include "llvm/MCA/HWEventListener.h"
#include "llvm/Support/raw_ostream.h"
+namespace llvm {
namespace mca {
class View : public HWEventListener {
@@ -28,5 +29,6 @@ public:
void anchor() override;
};
} // namespace mca
+} // namespace llvm
#endif
diff --git a/tools/llvm-mca/llvm-mca.cpp b/tools/llvm-mca/llvm-mca.cpp
index 897ff232a36d..68d63db599d7 100644
--- a/tools/llvm-mca/llvm-mca.cpp
+++ b/tools/llvm-mca/llvm-mca.cpp
@@ -22,26 +22,27 @@
//===----------------------------------------------------------------------===//
#include "CodeRegion.h"
-#include "Context.h"
-#include "DispatchStatistics.h"
-#include "FetchStage.h"
-#include "InstructionInfoView.h"
-#include "InstructionTables.h"
-#include "Pipeline.h"
+#include "CodeRegionGenerator.h"
#include "PipelinePrinter.h"
-#include "RegisterFileStatistics.h"
-#include "ResourcePressureView.h"
-#include "RetireControlUnitStatistics.h"
-#include "SchedulerStatistics.h"
-#include "SummaryView.h"
-#include "TimelineView.h"
+#include "Views/DispatchStatistics.h"
+#include "Views/InstructionInfoView.h"
+#include "Views/RegisterFileStatistics.h"
+#include "Views/ResourcePressureView.h"
+#include "Views/RetireControlUnitStatistics.h"
+#include "Views/SchedulerStatistics.h"
+#include "Views/SummaryView.h"
+#include "Views/TimelineView.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCObjectFileInfo.h"
-#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCRegisterInfo.h"
-#include "llvm/MC/MCStreamer.h"
+#include "llvm/MCA/Context.h"
+#include "llvm/MCA/Pipeline.h"
+#include "llvm/MCA/Stages/EntryStage.h"
+#include "llvm/MCA/Stages/InstructionTables.h"
+#include "llvm/MCA/Support.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
@@ -67,13 +68,13 @@ static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),
cl::value_desc("filename"));
static cl::opt<std::string>
- ArchName("march", cl::desc("Target arch to assemble for, "
- "see -version for available targets"),
+ ArchName("march", cl::desc("Target architecture. "
+ "See -version for available targets"),
cl::cat(ToolOptions));
static cl::opt<std::string>
- TripleName("mtriple", cl::desc("Target triple to assemble for, "
- "see -version for available targets"),
+ TripleName("mtriple",
+ cl::desc("Target triple. See -version for available targets"),
cl::cat(ToolOptions));
static cl::opt<std::string>
@@ -148,15 +149,13 @@ static cl::opt<bool>
cl::desc("If set, assume that loads and stores do not alias"),
cl::cat(ToolOptions), cl::init(true));
-static cl::opt<unsigned>
- LoadQueueSize("lqueue",
- cl::desc("Size of the load queue (unbound by default)"),
- cl::cat(ToolOptions), cl::init(0));
+static cl::opt<unsigned> LoadQueueSize("lqueue",
+ cl::desc("Size of the load queue"),
+ cl::cat(ToolOptions), cl::init(0));
-static cl::opt<unsigned>
- StoreQueueSize("squeue",
- cl::desc("Size of the store queue (unbound by default)"),
- cl::cat(ToolOptions), cl::init(0));
+static cl::opt<unsigned> StoreQueueSize("squeue",
+ cl::desc("Size of the store queue"),
+ cl::cat(ToolOptions), cl::init(0));
static cl::opt<bool>
PrintInstructionTables("instruction-tables",
@@ -180,7 +179,6 @@ static cl::opt<bool>
namespace {
const Target *getTarget(const char *ProgName) {
- TripleName = Triple::normalize(TripleName);
if (TripleName.empty())
TripleName = Triple::normalize(sys::getDefaultTargetTriple());
Triple TheTriple(TripleName);
@@ -198,59 +196,6 @@ const Target *getTarget(const char *ProgName) {
return TheTarget;
}
-// A comment consumer that parses strings.
-// The only valid tokens are strings.
-class MCACommentConsumer : public AsmCommentConsumer {
-public:
- mca::CodeRegions &Regions;
-
- MCACommentConsumer(mca::CodeRegions &R) : Regions(R) {}
- void HandleComment(SMLoc Loc, StringRef CommentText) override {
- // Skip empty comments.
- StringRef Comment(CommentText);
- if (Comment.empty())
- return;
-
- // Skip spaces and tabs
- unsigned Position = Comment.find_first_not_of(" \t");
- if (Position >= Comment.size())
- // we reached the end of the comment. Bail out.
- return;
-
- Comment = Comment.drop_front(Position);
- if (Comment.consume_front("LLVM-MCA-END")) {
- Regions.endRegion(Loc);
- return;
- }
-
- // Now try to parse string LLVM-MCA-BEGIN
- if (!Comment.consume_front("LLVM-MCA-BEGIN"))
- return;
-
- // Skip spaces and tabs
- Position = Comment.find_first_not_of(" \t");
- if (Position < Comment.size())
- Comment = Comment.drop_front(Position);
- // Use the rest of the string as a descriptor for this code snippet.
- Regions.beginRegion(Comment, Loc);
- }
-};
-
-int AssembleInput(const char *ProgName, MCAsmParser &Parser,
- const Target *TheTarget, MCSubtargetInfo &STI,
- MCInstrInfo &MCII, MCTargetOptions &MCOptions) {
- std::unique_ptr<MCTargetAsmParser> TAP(
- TheTarget->createMCAsmParser(STI, Parser, MCII, MCOptions));
-
- if (!TAP) {
- WithColor::error() << "this target does not support assembly parsing.\n";
- return 1;
- }
-
- Parser.setTargetParser(*TAP);
- return Parser.Run(false);
-}
-
ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() {
if (OutputFilename == "")
OutputFilename = "-";
@@ -261,40 +206,6 @@ ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() {
return std::move(Out);
return EC;
}
-
-class MCStreamerWrapper final : public MCStreamer {
- mca::CodeRegions &Regions;
-
-public:
- MCStreamerWrapper(MCContext &Context, mca::CodeRegions &R)
- : MCStreamer(Context), Regions(R) {}
-
- // We only want to intercept the emission of new instructions.
- virtual void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI,
- bool /* unused */) override {
- Regions.addInstruction(llvm::make_unique<const MCInst>(Inst));
- }
-
- bool EmitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
- return true;
- }
-
- void EmitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
- unsigned ByteAlignment) override {}
- void EmitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
- uint64_t Size = 0, unsigned ByteAlignment = 0,
- SMLoc Loc = SMLoc()) override {}
- void EmitGPRel32Value(const MCExpr *Value) override {}
- void BeginCOFFSymbolDef(const MCSymbol *Symbol) override {}
- void EmitCOFFSymbolStorageClass(int StorageClass) override {}
- void EmitCOFFSymbolType(int Type) override {}
- void EndCOFFSymbolDef() override {}
-
- const std::vector<std::unique_ptr<const MCInst>> &
- GetInstructionSequence(unsigned Index) const {
- return Regions.getInstructionSequence(Index);
- }
-};
} // end of anonymous namespace
static void processOptionImpl(cl::opt<bool> &O, const cl::opt<bool> &Default) {
@@ -318,20 +229,30 @@ static void processViewOptions() {
EnableAllViews.getPosition() < EnableAllStats.getPosition()
? EnableAllStats
: EnableAllViews;
- processOptionImpl(PrintSummaryView, Default);
processOptionImpl(PrintRegisterFileStats, Default);
processOptionImpl(PrintDispatchStats, Default);
processOptionImpl(PrintSchedulerStats, Default);
processOptionImpl(PrintRetireStats, Default);
}
+// Returns true on success.
+static bool runPipeline(mca::Pipeline &P) {
+ // Handle pipeline errors here.
+ Expected<unsigned> Cycles = P.run();
+ if (!Cycles) {
+ WithColor::error() << toString(Cycles.takeError());
+ return false;
+ }
+ return true;
+}
+
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
// Initialize targets and assembly parsers.
- llvm::InitializeAllTargetInfos();
- llvm::InitializeAllTargetMCs();
- llvm::InitializeAllAsmParsers();
+ InitializeAllTargetInfos();
+ InitializeAllTargetMCs();
+ InitializeAllAsmParsers();
// Enable printing of available targets when flag --version is specified.
cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
@@ -342,9 +263,6 @@ int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv,
"llvm machine code performance analyzer.\n");
- MCTargetOptions MCOptions;
- MCOptions.PreserveAsmComments = false;
-
// Get the target from the triple. If a triple is not specified, then select
// the default triple for the host. If the triple doesn't correspond to any
// registered target, then exit with an error message.
@@ -384,9 +302,6 @@ int main(int argc, char **argv) {
std::unique_ptr<buffer_ostream> BOS;
- mca::CodeRegions Regions(SrcMgr);
- MCStreamerWrapper Str(Ctx, Regions);
-
std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
std::unique_ptr<MCInstrAnalysis> MCIA(
@@ -419,14 +334,20 @@ int main(int argc, char **argv) {
return 1;
}
- std::unique_ptr<MCAsmParser> P(createMCAsmParser(SrcMgr, Ctx, Str, *MAI));
- MCAsmLexer &Lexer = P->getLexer();
- MCACommentConsumer CC(Regions);
- Lexer.setCommentConsumer(&CC);
-
- if (AssembleInput(ProgName, *P, TheTarget, *STI, *MCII, MCOptions))
+ // Parse the input and create CodeRegions that llvm-mca can analyze.
+ mca::AsmCodeRegionGenerator CRG(*TheTarget, SrcMgr, Ctx, *MAI, *STI, *MCII);
+ Expected<const mca::CodeRegions &> RegionsOrErr = CRG.parseCodeRegions();
+ if (!RegionsOrErr) {
+ if (auto Err =
+ handleErrors(RegionsOrErr.takeError(), [](const StringError &E) {
+ WithColor::error() << E.getMessage() << '\n';
+ })) {
+ // Default case.
+ WithColor::error() << toString(std::move(Err)) << '\n';
+ }
return 1;
-
+ }
+ const mca::CodeRegions &Regions = *RegionsOrErr;
if (Regions.empty()) {
WithColor::error() << "no assembly instructions found.\n";
return 1;
@@ -439,7 +360,7 @@ int main(int argc, char **argv) {
return 1;
}
- unsigned AssemblerDialect = P->getAssemblerDialect();
+ unsigned AssemblerDialect = CRG.getAssemblerDialect();
if (OutputAsmVariant >= 0)
AssemblerDialect = static_cast<unsigned>(OutputAsmVariant);
std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter(
@@ -452,7 +373,7 @@ int main(int argc, char **argv) {
return 1;
}
- std::unique_ptr<llvm::ToolOutputFile> TOF = std::move(*OF);
+ std::unique_ptr<ToolOutputFile> TOF = std::move(*OF);
const MCSchedModel &SM = STI->getSchedModel();
@@ -461,7 +382,7 @@ int main(int argc, char **argv) {
Width = DispatchWidth;
// Create an instruction builder.
- mca::InstrBuilder IB(*STI, *MCII, *MRI, *MCIA, *IP);
+ mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get());
// Create a context to control ownership of the pipeline hardware.
mca::Context MCA(*MRI, *STI);
@@ -471,6 +392,7 @@ int main(int argc, char **argv) {
// Number each region in the sequence.
unsigned RegionIdx = 0;
+
for (const std::unique_ptr<mca::CodeRegion> &Region : Regions) {
// Skip empty code regions.
if (Region->empty())
@@ -486,24 +408,53 @@ int main(int argc, char **argv) {
TOF->os() << "\n\n";
}
- mca::SourceMgr S(Region->getInstructions(),
- PrintInstructionTables ? 1 : Iterations);
+ // Lower the MCInst sequence into an mca::Instruction sequence.
+ ArrayRef<MCInst> Insts = Region->getInstructions();
+ std::vector<std::unique_ptr<mca::Instruction>> LoweredSequence;
+ for (const MCInst &MCI : Insts) {
+ Expected<std::unique_ptr<mca::Instruction>> Inst =
+ IB.createInstruction(MCI);
+ if (!Inst) {
+ if (auto NewE = handleErrors(
+ Inst.takeError(),
+ [&IP, &STI](const mca::InstructionError<MCInst> &IE) {
+ std::string InstructionStr;
+ raw_string_ostream SS(InstructionStr);
+ WithColor::error() << IE.Message << '\n';
+ IP->printInst(&IE.Inst, SS, "", *STI);
+ SS.flush();
+ WithColor::note() << "instruction: " << InstructionStr
+ << '\n';
+ })) {
+ // Default case.
+ WithColor::error() << toString(std::move(NewE));
+ }
+ return 1;
+ }
+
+ LoweredSequence.emplace_back(std::move(Inst.get()));
+ }
+
+ mca::SourceMgr S(LoweredSequence, PrintInstructionTables ? 1 : Iterations);
if (PrintInstructionTables) {
// Create a pipeline, stages, and a printer.
auto P = llvm::make_unique<mca::Pipeline>();
- P->appendStage(llvm::make_unique<mca::FetchStage>(IB, S));
- P->appendStage(llvm::make_unique<mca::InstructionTables>(SM, IB));
+ P->appendStage(llvm::make_unique<mca::EntryStage>(S));
+ P->appendStage(llvm::make_unique<mca::InstructionTables>(SM));
mca::PipelinePrinter Printer(*P);
// Create the views for this pipeline, execute, and emit a report.
if (PrintInstructionInfoView) {
- Printer.addView(
- llvm::make_unique<mca::InstructionInfoView>(*STI, *MCII, S, *IP));
+ Printer.addView(llvm::make_unique<mca::InstructionInfoView>(
+ *STI, *MCII, Insts, *IP));
}
Printer.addView(
- llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, S));
- P->run();
+ llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts));
+
+ if (!runPipeline(*P))
+ return 1;
+
Printer.printReport(TOF->os());
continue;
}
@@ -513,11 +464,11 @@ int main(int argc, char **argv) {
mca::PipelinePrinter Printer(*P);
if (PrintSummaryView)
- Printer.addView(llvm::make_unique<mca::SummaryView>(SM, S, Width));
+ Printer.addView(llvm::make_unique<mca::SummaryView>(SM, Insts, Width));
if (PrintInstructionInfoView)
Printer.addView(
- llvm::make_unique<mca::InstructionInfoView>(*STI, *MCII, S, *IP));
+ llvm::make_unique<mca::InstructionInfoView>(*STI, *MCII, Insts, *IP));
if (PrintDispatchStats)
Printer.addView(llvm::make_unique<mca::DispatchStatistics>());
@@ -526,21 +477,26 @@ int main(int argc, char **argv) {
Printer.addView(llvm::make_unique<mca::SchedulerStatistics>(*STI));
if (PrintRetireStats)
- Printer.addView(llvm::make_unique<mca::RetireControlUnitStatistics>());
+ Printer.addView(llvm::make_unique<mca::RetireControlUnitStatistics>(SM));
if (PrintRegisterFileStats)
Printer.addView(llvm::make_unique<mca::RegisterFileStatistics>(*STI));
if (PrintResourcePressureView)
Printer.addView(
- llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, S));
+ llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts));
if (PrintTimelineView) {
+ unsigned TimelineIterations =
+ TimelineMaxIterations ? TimelineMaxIterations : 10;
Printer.addView(llvm::make_unique<mca::TimelineView>(
- *STI, *IP, S, TimelineMaxIterations, TimelineMaxCycles));
+ *STI, *IP, Insts, std::min(TimelineIterations, S.getNumIterations()),
+ TimelineMaxCycles));
}
- P->run();
+ if (!runPipeline(*P))
+ return 1;
+
Printer.printReport(TOF->os());
// Clear the InstrBuilder internal state in preparation for another round.
diff --git a/tools/llvm-microsoft-demangle-fuzzer/CMakeLists.txt b/tools/llvm-microsoft-demangle-fuzzer/CMakeLists.txt
new file mode 100644
index 000000000000..d3db43a686bb
--- /dev/null
+++ b/tools/llvm-microsoft-demangle-fuzzer/CMakeLists.txt
@@ -0,0 +1,10 @@
+set(LLVM_LINK_COMPONENTS
+ Demangle
+ FuzzMutate
+ Support
+)
+
+add_llvm_fuzzer(llvm-microsoft-demangle-fuzzer
+ llvm-microsoft-demangle-fuzzer.cpp
+ DUMMY_MAIN DummyDemanglerFuzzer.cpp
+ )
diff --git a/tools/llvm-microsoft-demangle-fuzzer/DummyDemanglerFuzzer.cpp b/tools/llvm-microsoft-demangle-fuzzer/DummyDemanglerFuzzer.cpp
new file mode 100644
index 000000000000..a2bf9f1b807e
--- /dev/null
+++ b/tools/llvm-microsoft-demangle-fuzzer/DummyDemanglerFuzzer.cpp
@@ -0,0 +1,19 @@
+//===--- DummyDemanglerMain.cpp - Entry point to sanity check the fuzzer --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of main so we can build and test without linking libFuzzer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/FuzzMutate/FuzzerCLI.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+int main(int argc, char *argv[]) {
+ return llvm::runFuzzerOnInputs(argc, argv, LLVMFuzzerTestOneInput);
+}
diff --git a/tools/llvm-microsoft-demangle-fuzzer/llvm-microsoft-demangle-fuzzer.cpp b/tools/llvm-microsoft-demangle-fuzzer/llvm-microsoft-demangle-fuzzer.cpp
new file mode 100644
index 000000000000..4c1a4130e0b6
--- /dev/null
+++ b/tools/llvm-microsoft-demangle-fuzzer/llvm-microsoft-demangle-fuzzer.cpp
@@ -0,0 +1,21 @@
+//===--- llvm-demangle-fuzzer.cpp - Fuzzer for the Itanium Demangler ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Demangle/Demangle.h"
+
+#include <cstdint>
+#include <cstdlib>
+#include <string>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ std::string NullTerminatedString((const char *)Data, Size);
+ free(llvm::microsoftDemangle(NullTerminatedString.c_str(), nullptr, nullptr,
+ nullptr));
+ return 0;
+}
diff --git a/tools/llvm-mt/Opts.td b/tools/llvm-mt/Opts.td
index 6dc3eea524e6..da5b2c992ee3 100644
--- a/tools/llvm-mt/Opts.td
+++ b/tools/llvm-mt/Opts.td
@@ -23,6 +23,7 @@ def validate_file_hashes : Joined<["/", "-"], "validate_file_hashes:">, HelpText
def canonicalize : Flag<["/", "-"], "canonicalize:">, HelpText<"Not supported">, Group<unsupported>;
def check_for_duplicates : Flag<["/", "-"], "check_for_duplicates:">, HelpText<"Not supported">, Group<unsupported>;
def make_cdfs : Flag<["/", "-"], "makecdfs:">, HelpText<"Not supported">, Group<unsupported>;
+def notify_update : Flag<["/", "-"], "notify_update">, HelpText<"Not supported">, Group<unsupported>;
def verbose : Flag<["/", "-"], "verbose">, HelpText<"Not supported">, Group<unsupported>;
def help : Flag<["/", "-"], "?">;
def help_long : Flag<["/", "-"], "help">, Alias<help>;
diff --git a/tools/llvm-mt/llvm-mt.cpp b/tools/llvm-mt/llvm-mt.cpp
index 1339fd1572ec..ea75c012eac8 100644
--- a/tools/llvm-mt/llvm-mt.cpp
+++ b/tools/llvm-mt/llvm-mt.cpp
@@ -115,7 +115,7 @@ int main(int Argc, const char **Argv) {
}
if (InputArgs.hasArg(OPT_help)) {
- T.PrintHelp(outs(), "mt", "Manifest Tool", false);
+ T.PrintHelp(outs(), "llvm-mt [options] file...", "Manifest Tool", false);
return 0;
}
diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp
index 37c1bf85809e..042e284e8369 100644
--- a/tools/llvm-nm/llvm-nm.cpp
+++ b/tools/llvm-nm/llvm-nm.cpp
@@ -38,6 +38,7 @@
#include "llvm/Support/Program.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <vector>
@@ -90,6 +91,8 @@ cl::opt<bool> BSDFormat("B", cl::desc("Alias for --format=bsd"),
cl::Grouping);
cl::opt<bool> POSIXFormat("P", cl::desc("Alias for --format=posix"),
cl::Grouping);
+cl::alias Portability("portability", cl::desc("Alias for --format=posix"),
+ cl::aliasopt(POSIXFormat), cl::NotHidden);
cl::opt<bool> DarwinFormat("m", cl::desc("Alias for --format=darwin"),
cl::Grouping);
@@ -183,6 +186,8 @@ cl::opt<bool> DyldInfoOnly("dyldinfo-only",
cl::opt<bool> NoLLVMBitcode("no-llvm-bc",
cl::desc("Disable LLVM bitcode reader"));
+cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
+
bool PrintAddress = true;
bool MultipleFiles = false;
@@ -194,7 +199,7 @@ std::string ToolName;
static void error(Twine Message, Twine Path = Twine()) {
HadError = true;
- errs() << ToolName << ": " << Path << ": " << Message << ".\n";
+ WithColor::error(errs(), ToolName) << Path << ": " << Message << ".\n";
}
static bool error(std::error_code EC, Twine Path = Twine()) {
@@ -207,11 +212,11 @@ static bool error(std::error_code EC, Twine Path = Twine()) {
// This version of error() prints the archive name and member name, for example:
// "libx.a(foo.o)" after the ToolName before the error message. It sets
-// HadError but returns allowing the code to move on to other archive members.
+// HadError but returns allowing the code to move on to other archive members.
static void error(llvm::Error E, StringRef FileName, const Archive::Child &C,
StringRef ArchitectureName = StringRef()) {
HadError = true;
- errs() << ToolName << ": " << FileName;
+ WithColor::error(errs(), ToolName) << FileName;
Expected<StringRef> NameOrErr = C.getName();
// TODO: if we have a error getting the name then it would be nice to print
@@ -228,7 +233,7 @@ static void error(llvm::Error E, StringRef FileName, const Archive::Child &C,
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(std::move(E), OS, "");
+ logAllUnhandledErrors(std::move(E), OS);
OS.flush();
errs() << " " << Buf << "\n";
}
@@ -236,18 +241,18 @@ static void error(llvm::Error E, StringRef FileName, const Archive::Child &C,
// This version of error() prints the file name and which architecture slice it
// is from, for example: "foo.o (for architecture i386)" after the ToolName
// before the error message. It sets HadError but returns allowing the code to
-// move on to other architecture slices.
+// move on to other architecture slices.
static void error(llvm::Error E, StringRef FileName,
StringRef ArchitectureName = StringRef()) {
HadError = true;
- errs() << ToolName << ": " << FileName;
+ WithColor::error(errs(), ToolName) << FileName;
if (!ArchitectureName.empty())
errs() << " (for architecture " << ArchitectureName << ") ";
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(std::move(E), OS, "");
+ logAllUnhandledErrors(std::move(E), OS);
OS.flush();
errs() << " " << Buf << "\n";
}
@@ -674,7 +679,7 @@ static void darwinPrintStab(MachOObjectFile *MachO, SymbolListT::iterator I) {
}
static Optional<std::string> demangle(StringRef Name, bool StripUnderscore) {
- if (StripUnderscore && Name.size() > 0 && Name[0] == '_')
+ if (StripUnderscore && !Name.empty() && Name[0] == '_')
Name = Name.substr(1);
if (!Name.startswith("_Z"))
@@ -709,7 +714,7 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
if (ReverseSort)
Cmp = [=](const NMSymbol &A, const NMSymbol &B) { return Cmp(B, A); };
- llvm::sort(SymbolList.begin(), SymbolList.end(), Cmp);
+ llvm::sort(SymbolList, Cmp);
}
if (!PrintFileName) {
@@ -757,6 +762,24 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
}
}
+ auto writeFileName = [&](raw_ostream &S) {
+ if (!ArchitectureName.empty())
+ S << "(for architecture " << ArchitectureName << "):";
+ if (OutputFormat == posix && !ArchiveName.empty())
+ S << ArchiveName << "[" << CurrentFilename << "]: ";
+ else {
+ if (!ArchiveName.empty())
+ S << ArchiveName << ":";
+ S << CurrentFilename << ": ";
+ }
+ };
+
+ if (SymbolList.empty()) {
+ if (PrintFileName)
+ writeFileName(errs());
+ errs() << "no symbols\n";
+ }
+
for (SymbolListT::iterator I = SymbolList.begin(), E = SymbolList.end();
I != E; ++I) {
uint32_t SymFlags;
@@ -778,17 +801,8 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
(!Global && ExternalOnly) || (SizeSort && !PrintAddress) ||
(Weak && NoWeakSymbols))
continue;
- if (PrintFileName) {
- if (!ArchitectureName.empty())
- outs() << "(for architecture " << ArchitectureName << "):";
- if (OutputFormat == posix && !ArchiveName.empty())
- outs() << ArchiveName << "[" << CurrentFilename << "]: ";
- else {
- if (!ArchiveName.empty())
- outs() << ArchiveName << ":";
- outs() << CurrentFilename << ": ";
- }
- }
+ if (PrintFileName)
+ writeFileName(outs());
if ((JustSymbolName ||
(UndefinedOnly && MachO && OutputFormat != darwin)) &&
OutputFormat != posix) {
@@ -1018,8 +1032,7 @@ static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) {
StringRef SectionName;
Obj.getSectionName(Ref, SectionName);
StringRef SegmentName = Obj.getSectionFinalSegmentName(Ref);
- if (Obj.is64Bit() &&
- Obj.getHeader64().filetype == MachO::MH_KEXT_BUNDLE &&
+ if (Obj.is64Bit() && Obj.getHeader64().filetype == MachO::MH_KEXT_BUNDLE &&
SegmentName == "__TEXT_EXEC" && SectionName == "__text")
return 't';
if (SegmentName == "__TEXT" && SectionName == "__text")
@@ -1152,7 +1165,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
// file get the section number for that section in this object file.
unsigned int Nsect = 0;
MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj);
- if (SegSect.size() != 0 && MachO) {
+ if (!SegSect.empty() && MachO) {
Nsect = getNsectForSegSect(MachO);
// If this section is not in the object file no symbols are printed.
if (Nsect == 0)
@@ -1170,8 +1183,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
// see if this symbol is a symbol from that section and if not skip it.
if (Nsect && Nsect != getNsectInMachO(*MachO, Sym))
continue;
- NMSymbol S;
- memset(&S, '\0', sizeof(S));
+ NMSymbol S = {};
S.Size = 0;
S.Address = 0;
if (PrintSize) {
@@ -1265,8 +1277,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
}
}
if (!found) {
- NMSymbol S;
- memset(&S, '\0', sizeof(NMSymbol));
+ NMSymbol S = {};
S.Address = Entry.address() + BaseSegmentAddress;
S.Size = 0;
S.TypeChar = '\0';
@@ -1356,8 +1367,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
// Now create the undefined symbol using the referened dynamic
// library.
- NMSymbol U;
- memset(&U, '\0', sizeof(NMSymbol));
+ NMSymbol U = {};
U.Address = 0;
U.Size = 0;
U.TypeChar = 'U';
@@ -1423,8 +1433,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
}
if (!found) {
LastSymbolName = Entry.symbolName();
- NMSymbol B;
- memset(&B, '\0', sizeof(NMSymbol));
+ NMSymbol B = {};
B.Address = 0;
B.Size = 0;
B.TypeChar = 'U';
@@ -1483,8 +1492,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
}
if (!found) {
LastSymbolName = Entry.symbolName();
- NMSymbol L;
- memset(&L, '\0', sizeof(NMSymbol));
+ NMSymbol L = {};
L.Name = Entry.symbolName();
L.Address = 0;
L.Size = 0;
@@ -1600,7 +1608,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
uint64_t lc_main_offset = UINT64_MAX;
for (const auto &Command : MachO->load_commands()) {
if (Command.C.cmd == MachO::LC_FUNCTION_STARTS) {
- // We found a function starts segment, parse the addresses for
+ // We found a function starts segment, parse the addresses for
// consumption.
MachO::linkedit_data_command LLC =
MachO->getLinkeditDataLoadCommand(Command);
@@ -1622,9 +1630,8 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
}
// See this address is not already in the symbol table fake up an
// nlist for it.
- if (!found) {
- NMSymbol F;
- memset(&F, '\0', sizeof(NMSymbol));
+ if (!found) {
+ NMSymbol F = {};
F.Name = "<redacted function X>";
F.Address = FoundFns[f] + BaseSegmentAddress;
F.Size = 0;
@@ -1744,12 +1751,14 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
outs() << "Archive map\n";
for (; I != E; ++I) {
Expected<Archive::Child> C = I->getMember();
- if (!C)
+ if (!C) {
error(C.takeError(), Filename);
+ break;
+ }
Expected<StringRef> FileNameOrErr = C->getName();
if (!FileNameOrErr) {
error(FileNameOrErr.takeError(), Filename);
- return;
+ break;
}
StringRef SymName = I->getName();
outs() << SymName << " in " << FileNameOrErr.get() << "\n";
@@ -1769,8 +1778,8 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
}
if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
if (!MachOPrintSizeWarning && PrintSize && isa<MachOObjectFile>(O)) {
- errs() << ToolName << ": warning sizes with -print-size for Mach-O "
- "files are always zero.\n";
+ WithColor::warning(errs(), ToolName)
+ << "sizes with -print-size for Mach-O files are always zero.\n";
MachOPrintSizeWarning = true;
}
if (!checkMachOAndArchFlags(O, Filename))
@@ -1793,7 +1802,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
}
if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) {
// If we have a list of architecture flags specified dump only those.
- if (!ArchAll && ArchFlags.size() != 0) {
+ if (!ArchAll && !ArchFlags.empty()) {
// Look for a slice in the universal binary that matches each ArchFlag.
bool ArchFound;
for (unsigned i = 0; i < ArchFlags.size(); ++i) {
@@ -1882,14 +1891,14 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
// No architecture flags were specified so if this contains a slice that
// matches the host architecture dump only that.
if (!ArchAll) {
- StringRef HostArchName = MachOObjectFile::getHostArch().getArchName();
+ Triple HostTriple = MachOObjectFile::getHostArch();
+ StringRef HostArchName = HostTriple.getArchName();
for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
E = UB->end_objects();
I != E; ++I) {
if (HostArchName == I->getArchFlagName()) {
Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
std::string ArchiveName;
- ArchiveName.clear();
if (ObjOrErr) {
ObjectFile &Obj = *ObjOrErr.get();
dumpSymbolNamesFromObject(Obj, false);
@@ -2011,8 +2020,8 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
}
if (SymbolicFile *O = dyn_cast<SymbolicFile>(&Bin)) {
if (!MachOPrintSizeWarning && PrintSize && isa<MachOObjectFile>(O)) {
- errs() << ToolName << ": warning sizes with -print-size for Mach-O files "
- "are always zero.\n";
+ WithColor::warning(errs(), ToolName)
+ << "sizes with -print-size for Mach-O files are always zero.\n";
MachOPrintSizeWarning = true;
}
if (!checkMachOAndArchFlags(O, Filename))
@@ -2064,7 +2073,7 @@ int main(int argc, char **argv) {
}
}
- if (SegSect.size() != 0 && SegSect.size() != 2)
+ if (!SegSect.empty() && SegSect.size() != 2)
error("bad number of arguments (must be two arguments)",
"for the -s option");
diff --git a/tools/llvm-objcopy/Buffer.cpp b/tools/llvm-objcopy/Buffer.cpp
new file mode 100644
index 000000000000..8044b023aaad
--- /dev/null
+++ b/tools/llvm-objcopy/Buffer.cpp
@@ -0,0 +1,51 @@
+//===- Buffer.cpp ---------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Buffer.h"
+#include "llvm-objcopy.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <memory>
+
+namespace llvm {
+namespace objcopy {
+
+Buffer::~Buffer() {}
+
+void FileBuffer::allocate(size_t Size) {
+ Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+ FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable);
+ handleAllErrors(BufferOrErr.takeError(), [this](const ErrorInfoBase &E) {
+ error("failed to open " + getName() + ": " + E.message());
+ });
+ Buf = std::move(*BufferOrErr);
+}
+
+Error FileBuffer::commit() { return Buf->commit(); }
+
+uint8_t *FileBuffer::getBufferStart() {
+ return reinterpret_cast<uint8_t *>(Buf->getBufferStart());
+}
+
+void MemBuffer::allocate(size_t Size) {
+ Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName());
+}
+
+Error MemBuffer::commit() { return Error::success(); }
+
+uint8_t *MemBuffer::getBufferStart() {
+ return reinterpret_cast<uint8_t *>(Buf->getBufferStart());
+}
+
+std::unique_ptr<WritableMemoryBuffer> MemBuffer::releaseMemoryBuffer() {
+ return std::move(Buf);
+}
+
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/tools/llvm-objcopy/Buffer.h b/tools/llvm-objcopy/Buffer.h
new file mode 100644
index 000000000000..e5b9c5b2d22b
--- /dev/null
+++ b/tools/llvm-objcopy/Buffer.h
@@ -0,0 +1,66 @@
+//===- Buffer.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_BUFFER_H
+#define LLVM_TOOLS_OBJCOPY_BUFFER_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <memory>
+
+namespace llvm {
+namespace objcopy {
+
+// The class Buffer abstracts out the common interface of FileOutputBuffer and
+// WritableMemoryBuffer so that the hierarchy of Writers depends on this
+// abstract interface and doesn't depend on a particular implementation.
+// TODO: refactor the buffer classes in LLVM to enable us to use them here
+// directly.
+class Buffer {
+ StringRef Name;
+
+public:
+ virtual ~Buffer();
+ virtual void allocate(size_t Size) = 0;
+ virtual uint8_t *getBufferStart() = 0;
+ virtual Error commit() = 0;
+
+ explicit Buffer(StringRef Name) : Name(Name) {}
+ StringRef getName() const { return Name; }
+};
+
+class FileBuffer : public Buffer {
+ std::unique_ptr<FileOutputBuffer> Buf;
+
+public:
+ void allocate(size_t Size) override;
+ uint8_t *getBufferStart() override;
+ Error commit() override;
+
+ explicit FileBuffer(StringRef FileName) : Buffer(FileName) {}
+};
+
+class MemBuffer : public Buffer {
+ std::unique_ptr<WritableMemoryBuffer> Buf;
+
+public:
+ void allocate(size_t Size) override;
+ uint8_t *getBufferStart() override;
+ Error commit() override;
+
+ explicit MemBuffer(StringRef Name) : Buffer(Name) {}
+
+ std::unique_ptr<WritableMemoryBuffer> releaseMemoryBuffer();
+};
+
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OBJCOPY_BUFFER_H
diff --git a/tools/llvm-objcopy/CMakeLists.txt b/tools/llvm-objcopy/CMakeLists.txt
index b0cd66be5b3a..1beb7374cf89 100644
--- a/tools/llvm-objcopy/CMakeLists.txt
+++ b/tools/llvm-objcopy/CMakeLists.txt
@@ -14,8 +14,15 @@ tablegen(LLVM StripOpts.inc -gen-opt-parser-defs)
add_public_tablegen_target(StripOptsTableGen)
add_llvm_tool(llvm-objcopy
+ Buffer.cpp
+ CopyConfig.cpp
llvm-objcopy.cpp
- Object.cpp
+ COFF/COFFObjcopy.cpp
+ COFF/Object.cpp
+ COFF/Reader.cpp
+ COFF/Writer.cpp
+ ELF/ELFObjcopy.cpp
+ ELF/Object.cpp
DEPENDS
ObjcopyOptsTableGen
StripOptsTableGen
diff --git a/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
new file mode 100644
index 000000000000..6b386d29979c
--- /dev/null
+++ b/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -0,0 +1,98 @@
+//===- COFFObjcopy.cpp ----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "COFFObjcopy.h"
+#include "Buffer.h"
+#include "CopyConfig.h"
+#include "Object.h"
+#include "Reader.h"
+#include "Writer.h"
+#include "llvm-objcopy.h"
+
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/Errc.h"
+#include <cassert>
+
+namespace llvm {
+namespace objcopy {
+namespace coff {
+
+using namespace object;
+using namespace COFF;
+
+static Error handleArgs(const CopyConfig &Config, Object &Obj) {
+ // StripAll removes all symbols and thus also removes all relocations.
+ if (Config.StripAll || Config.StripAllGNU)
+ for (Section &Sec : Obj.Sections)
+ Sec.Relocs.clear();
+
+ // If we need to do per-symbol removals, initialize the Referenced field.
+ if (Config.StripUnneeded || Config.DiscardAll ||
+ !Config.SymbolsToRemove.empty())
+ if (Error E = Obj.markSymbols())
+ return E;
+
+ // Actually do removals of symbols.
+ Obj.removeSymbols([&](const Symbol &Sym) {
+ // For StripAll, all relocations have been stripped and we remove all
+ // symbols.
+ if (Config.StripAll || Config.StripAllGNU)
+ return true;
+
+ if (is_contained(Config.SymbolsToRemove, Sym.Name)) {
+ // Explicitly removing a referenced symbol is an error.
+ if (Sym.Referenced)
+ reportError(Config.OutputFilename,
+ make_error<StringError>(
+ "not stripping symbol '" + Sym.Name +
+ "' because it is named in a relocation.",
+ llvm::errc::invalid_argument));
+ return true;
+ }
+
+ if (!Sym.Referenced) {
+ // With --strip-unneeded, GNU objcopy removes all unreferenced local
+ // symbols, and any unreferenced undefined external.
+ if (Config.StripUnneeded &&
+ (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC ||
+ Sym.Sym.SectionNumber == 0))
+ return true;
+
+ // GNU objcopy keeps referenced local symbols and external symbols
+ // if --discard-all is set, similar to what --strip-unneeded does,
+ // but undefined local symbols are kept when --discard-all is set.
+ if (Config.DiscardAll && Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC &&
+ Sym.Sym.SectionNumber != 0)
+ return true;
+ }
+
+ return false;
+ });
+ return Error::success();
+}
+
+void executeObjcopyOnBinary(const CopyConfig &Config,
+ object::COFFObjectFile &In, Buffer &Out) {
+ COFFReader Reader(In);
+ Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create();
+ if (!ObjOrErr)
+ reportError(Config.InputFilename, ObjOrErr.takeError());
+ Object *Obj = ObjOrErr->get();
+ assert(Obj && "Unable to deserialize COFF object");
+ if (Error E = handleArgs(Config, *Obj))
+ reportError(Config.InputFilename, std::move(E));
+ COFFWriter Writer(*Obj, Out);
+ if (Error E = Writer.write())
+ reportError(Config.OutputFilename, std::move(E));
+}
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/tools/llvm-objcopy/COFF/COFFObjcopy.h b/tools/llvm-objcopy/COFF/COFFObjcopy.h
new file mode 100644
index 000000000000..bf70bd9b4d84
--- /dev/null
+++ b/tools/llvm-objcopy/COFF/COFFObjcopy.h
@@ -0,0 +1,31 @@
+//===- COFFObjcopy.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H
+#define LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H
+
+namespace llvm {
+
+namespace object {
+class COFFObjectFile;
+} // end namespace object
+
+namespace objcopy {
+struct CopyConfig;
+class Buffer;
+
+namespace coff {
+void executeObjcopyOnBinary(const CopyConfig &Config,
+ object::COFFObjectFile &In, Buffer &Out);
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H
diff --git a/tools/llvm-objcopy/COFF/Object.cpp b/tools/llvm-objcopy/COFF/Object.cpp
new file mode 100644
index 000000000000..315d3a778623
--- /dev/null
+++ b/tools/llvm-objcopy/COFF/Object.cpp
@@ -0,0 +1,70 @@
+//===- Object.cpp ---------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Object.h"
+#include <algorithm>
+
+namespace llvm {
+namespace objcopy {
+namespace coff {
+
+using namespace object;
+
+void Object::addSymbols(ArrayRef<Symbol> NewSymbols) {
+ for (Symbol S : NewSymbols) {
+ S.UniqueId = NextSymbolUniqueId++;
+ Symbols.emplace_back(S);
+ }
+ updateSymbols();
+}
+
+void Object::updateSymbols() {
+ SymbolMap = DenseMap<size_t, Symbol *>(Symbols.size());
+ size_t RawSymIndex = 0;
+ for (Symbol &Sym : Symbols) {
+ SymbolMap[Sym.UniqueId] = &Sym;
+ Sym.RawIndex = RawSymIndex;
+ RawSymIndex += 1 + Sym.Sym.NumberOfAuxSymbols;
+ }
+}
+
+const Symbol *Object::findSymbol(size_t UniqueId) const {
+ auto It = SymbolMap.find(UniqueId);
+ if (It == SymbolMap.end())
+ return nullptr;
+ return It->second;
+}
+
+void Object::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) {
+ Symbols.erase(
+ std::remove_if(std::begin(Symbols), std::end(Symbols),
+ [ToRemove](const Symbol &Sym) { return ToRemove(Sym); }),
+ std::end(Symbols));
+ updateSymbols();
+}
+
+Error Object::markSymbols() {
+ for (Symbol &Sym : Symbols)
+ Sym.Referenced = false;
+ for (const Section &Sec : Sections) {
+ for (const Relocation &R : Sec.Relocs) {
+ auto It = SymbolMap.find(R.Target);
+ if (It == SymbolMap.end())
+ return make_error<StringError>("Relocation target " + Twine(R.Target) +
+ " not found",
+ object_error::invalid_symbol_index);
+ It->second->Referenced = true;
+ }
+ }
+ return Error::success();
+}
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/tools/llvm-objcopy/COFF/Object.h b/tools/llvm-objcopy/COFF/Object.h
new file mode 100644
index 000000000000..7531fb4cf39e
--- /dev/null
+++ b/tools/llvm-objcopy/COFF/Object.h
@@ -0,0 +1,148 @@
+//===- Object.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H
+#define LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/Object/COFF.h"
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+namespace coff {
+
+struct Relocation {
+ Relocation() {}
+ Relocation(const object::coff_relocation& R) : Reloc(R) {}
+
+ object::coff_relocation Reloc;
+ size_t Target;
+ StringRef TargetName; // Used for diagnostics only
+};
+
+struct Section {
+ object::coff_section Header;
+ ArrayRef<uint8_t> Contents;
+ std::vector<Relocation> Relocs;
+ StringRef Name;
+};
+
+struct Symbol {
+ object::coff_symbol32 Sym;
+ StringRef Name;
+ ArrayRef<uint8_t> AuxData;
+ size_t UniqueId;
+ size_t RawIndex;
+ bool Referenced;
+};
+
+struct Object {
+ bool IsPE = false;
+
+ object::dos_header DosHeader;
+ ArrayRef<uint8_t> DosStub;
+
+ object::coff_file_header CoffFileHeader;
+
+ bool Is64 = false;
+ object::pe32plus_header PeHeader;
+ uint32_t BaseOfData = 0; // pe32plus_header lacks this field.
+
+ std::vector<object::data_directory> DataDirectories;
+ std::vector<Section> Sections;
+
+ ArrayRef<Symbol> getSymbols() const { return Symbols; }
+ // This allows mutating individual Symbols, but not mutating the list
+ // of symbols itself.
+ iterator_range<std::vector<Symbol>::iterator> getMutableSymbols() {
+ return make_range(Symbols.begin(), Symbols.end());
+ }
+
+ const Symbol *findSymbol(size_t UniqueId) const;
+
+ void addSymbols(ArrayRef<Symbol> NewSymbols);
+ void removeSymbols(function_ref<bool(const Symbol &)> ToRemove);
+
+ // Set the Referenced field on all Symbols, based on relocations in
+ // all sections.
+ Error markSymbols();
+
+private:
+ std::vector<Symbol> Symbols;
+ DenseMap<size_t, Symbol *> SymbolMap;
+
+ size_t NextSymbolUniqueId = 0;
+
+ // Update SymbolMap and RawIndex in each Symbol.
+ void updateSymbols();
+};
+
+// Copy between coff_symbol16 and coff_symbol32.
+// The source and destination files can use either coff_symbol16 or
+// coff_symbol32, while we always store them as coff_symbol32 in the
+// intermediate data structure.
+template <class Symbol1Ty, class Symbol2Ty>
+void copySymbol(Symbol1Ty &Dest, const Symbol2Ty &Src) {
+ static_assert(sizeof(Dest.Name.ShortName) == sizeof(Src.Name.ShortName),
+ "Mismatched name sizes");
+ memcpy(Dest.Name.ShortName, Src.Name.ShortName, sizeof(Dest.Name.ShortName));
+ Dest.Value = Src.Value;
+ Dest.SectionNumber = Src.SectionNumber;
+ Dest.Type = Src.Type;
+ Dest.StorageClass = Src.StorageClass;
+ Dest.NumberOfAuxSymbols = Src.NumberOfAuxSymbols;
+}
+
+// Copy between pe32_header and pe32plus_header.
+// We store the intermediate state in a pe32plus_header.
+template <class PeHeader1Ty, class PeHeader2Ty>
+void copyPeHeader(PeHeader1Ty &Dest, const PeHeader2Ty &Src) {
+ Dest.Magic = Src.Magic;
+ Dest.MajorLinkerVersion = Src.MajorLinkerVersion;
+ Dest.MinorLinkerVersion = Src.MinorLinkerVersion;
+ Dest.SizeOfCode = Src.SizeOfCode;
+ Dest.SizeOfInitializedData = Src.SizeOfInitializedData;
+ Dest.SizeOfUninitializedData = Src.SizeOfUninitializedData;
+ Dest.AddressOfEntryPoint = Src.AddressOfEntryPoint;
+ Dest.BaseOfCode = Src.BaseOfCode;
+ Dest.ImageBase = Src.ImageBase;
+ Dest.SectionAlignment = Src.SectionAlignment;
+ Dest.FileAlignment = Src.FileAlignment;
+ Dest.MajorOperatingSystemVersion = Src.MajorOperatingSystemVersion;
+ Dest.MinorOperatingSystemVersion = Src.MinorOperatingSystemVersion;
+ Dest.MajorImageVersion = Src.MajorImageVersion;
+ Dest.MinorImageVersion = Src.MinorImageVersion;
+ Dest.MajorSubsystemVersion = Src.MajorSubsystemVersion;
+ Dest.MinorSubsystemVersion = Src.MinorSubsystemVersion;
+ Dest.Win32VersionValue = Src.Win32VersionValue;
+ Dest.SizeOfImage = Src.SizeOfImage;
+ Dest.SizeOfHeaders = Src.SizeOfHeaders;
+ Dest.CheckSum = Src.CheckSum;
+ Dest.Subsystem = Src.Subsystem;
+ Dest.DLLCharacteristics = Src.DLLCharacteristics;
+ Dest.SizeOfStackReserve = Src.SizeOfStackReserve;
+ Dest.SizeOfStackCommit = Src.SizeOfStackCommit;
+ Dest.SizeOfHeapReserve = Src.SizeOfHeapReserve;
+ Dest.SizeOfHeapCommit = Src.SizeOfHeapCommit;
+ Dest.LoaderFlags = Src.LoaderFlags;
+ Dest.NumberOfRvaAndSize = Src.NumberOfRvaAndSize;
+}
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H
diff --git a/tools/llvm-objcopy/COFF/Reader.cpp b/tools/llvm-objcopy/COFF/Reader.cpp
new file mode 100644
index 000000000000..a01768392d7d
--- /dev/null
+++ b/tools/llvm-objcopy/COFF/Reader.cpp
@@ -0,0 +1,171 @@
+//===- Reader.cpp ---------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Reader.h"
+#include "Object.h"
+#include "llvm-objcopy.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <cstddef>
+#include <cstdint>
+
+namespace llvm {
+namespace objcopy {
+namespace coff {
+
+using namespace object;
+
+Error COFFReader::readExecutableHeaders(Object &Obj) const {
+ const dos_header *DH = COFFObj.getDOSHeader();
+ Obj.Is64 = COFFObj.is64();
+ if (!DH)
+ return Error::success();
+
+ Obj.IsPE = true;
+ Obj.DosHeader = *DH;
+ if (DH->AddressOfNewExeHeader > sizeof(*DH))
+ Obj.DosStub = ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&DH[1]),
+ DH->AddressOfNewExeHeader - sizeof(*DH));
+
+ if (COFFObj.is64()) {
+ const pe32plus_header *PE32Plus = nullptr;
+ if (auto EC = COFFObj.getPE32PlusHeader(PE32Plus))
+ return errorCodeToError(EC);
+ Obj.PeHeader = *PE32Plus;
+ } else {
+ const pe32_header *PE32 = nullptr;
+ if (auto EC = COFFObj.getPE32Header(PE32))
+ return errorCodeToError(EC);
+ copyPeHeader(Obj.PeHeader, *PE32);
+ // The pe32plus_header (stored in Object) lacks the BaseOfData field.
+ Obj.BaseOfData = PE32->BaseOfData;
+ }
+
+ for (size_t I = 0; I < Obj.PeHeader.NumberOfRvaAndSize; I++) {
+ const data_directory *Dir;
+ if (auto EC = COFFObj.getDataDirectory(I, Dir))
+ return errorCodeToError(EC);
+ Obj.DataDirectories.emplace_back(*Dir);
+ }
+ return Error::success();
+}
+
+Error COFFReader::readSections(Object &Obj) const {
+ // Section indexing starts from 1.
+ for (size_t I = 1, E = COFFObj.getNumberOfSections(); I <= E; I++) {
+ const coff_section *Sec;
+ if (auto EC = COFFObj.getSection(I, Sec))
+ return errorCodeToError(EC);
+ Obj.Sections.push_back(Section());
+ Section &S = Obj.Sections.back();
+ S.Header = *Sec;
+ if (auto EC = COFFObj.getSectionContents(Sec, S.Contents))
+ return errorCodeToError(EC);
+ ArrayRef<coff_relocation> Relocs = COFFObj.getRelocations(Sec);
+ for (const coff_relocation &R : Relocs)
+ S.Relocs.push_back(R);
+ if (auto EC = COFFObj.getSectionName(Sec, S.Name))
+ return errorCodeToError(EC);
+ if (Sec->hasExtendedRelocations())
+ return make_error<StringError>("Extended relocations not supported yet",
+ object_error::parse_failed);
+ }
+ return Error::success();
+}
+
+Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const {
+ std::vector<Symbol> Symbols;
+ Symbols.reserve(COFFObj.getRawNumberOfSymbols());
+ for (uint32_t I = 0, E = COFFObj.getRawNumberOfSymbols(); I < E;) {
+ Expected<COFFSymbolRef> SymOrErr = COFFObj.getSymbol(I);
+ if (!SymOrErr)
+ return SymOrErr.takeError();
+ COFFSymbolRef SymRef = *SymOrErr;
+
+ Symbols.push_back(Symbol());
+ Symbol &Sym = Symbols.back();
+ // Copy symbols from the original form into an intermediate coff_symbol32.
+ if (IsBigObj)
+ copySymbol(Sym.Sym,
+ *reinterpret_cast<const coff_symbol32 *>(SymRef.getRawPtr()));
+ else
+ copySymbol(Sym.Sym,
+ *reinterpret_cast<const coff_symbol16 *>(SymRef.getRawPtr()));
+ if (auto EC = COFFObj.getSymbolName(SymRef, Sym.Name))
+ return errorCodeToError(EC);
+ Sym.AuxData = COFFObj.getSymbolAuxData(SymRef);
+ assert((Sym.AuxData.size() %
+ (IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16))) == 0);
+ I += 1 + SymRef.getNumberOfAuxSymbols();
+ }
+ Obj.addSymbols(Symbols);
+ return Error::success();
+}
+
+Error COFFReader::setRelocTargets(Object &Obj) const {
+ std::vector<const Symbol *> RawSymbolTable;
+ for (const Symbol &Sym : Obj.getSymbols()) {
+ RawSymbolTable.push_back(&Sym);
+ for (size_t I = 0; I < Sym.Sym.NumberOfAuxSymbols; I++)
+ RawSymbolTable.push_back(nullptr);
+ }
+ for (Section &Sec : Obj.Sections) {
+ for (Relocation &R : Sec.Relocs) {
+ if (R.Reloc.SymbolTableIndex >= RawSymbolTable.size())
+ return make_error<StringError>("SymbolTableIndex out of range",
+ object_error::parse_failed);
+ const Symbol *Sym = RawSymbolTable[R.Reloc.SymbolTableIndex];
+ if (Sym == nullptr)
+ return make_error<StringError>("Invalid SymbolTableIndex",
+ object_error::parse_failed);
+ R.Target = Sym->UniqueId;
+ R.TargetName = Sym->Name;
+ }
+ }
+ return Error::success();
+}
+
+Expected<std::unique_ptr<Object>> COFFReader::create() const {
+ auto Obj = llvm::make_unique<Object>();
+
+ const coff_file_header *CFH = nullptr;
+ const coff_bigobj_file_header *CBFH = nullptr;
+ COFFObj.getCOFFHeader(CFH);
+ COFFObj.getCOFFBigObjHeader(CBFH);
+ bool IsBigObj = false;
+ if (CFH) {
+ Obj->CoffFileHeader = *CFH;
+ } else {
+ if (!CBFH)
+ return make_error<StringError>("No COFF file header returned",
+ object_error::parse_failed);
+ // Only copying the few fields from the bigobj header that we need
+ // and won't recreate in the end.
+ Obj->CoffFileHeader.Machine = CBFH->Machine;
+ Obj->CoffFileHeader.TimeDateStamp = CBFH->TimeDateStamp;
+ IsBigObj = true;
+ }
+
+ if (Error E = readExecutableHeaders(*Obj))
+ return std::move(E);
+ if (Error E = readSections(*Obj))
+ return std::move(E);
+ if (Error E = readSymbols(*Obj, IsBigObj))
+ return std::move(E);
+ if (Error E = setRelocTargets(*Obj))
+ return std::move(E);
+
+ return std::move(Obj);
+}
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/tools/llvm-objcopy/COFF/Reader.h b/tools/llvm-objcopy/COFF/Reader.h
new file mode 100644
index 000000000000..ca7057d08c9f
--- /dev/null
+++ b/tools/llvm-objcopy/COFF/Reader.h
@@ -0,0 +1,43 @@
+//===- Reader.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_COFF_READER_H
+#define LLVM_TOOLS_OBJCOPY_COFF_READER_H
+
+#include "Buffer.h"
+#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace objcopy {
+namespace coff {
+
+struct Object;
+
+using object::COFFObjectFile;
+
+class COFFReader {
+ const COFFObjectFile &COFFObj;
+
+ Error readExecutableHeaders(Object &Obj) const;
+ Error readSections(Object &Obj) const;
+ Error readSymbols(Object &Obj, bool IsBigObj) const;
+ Error setRelocTargets(Object &Obj) const;
+
+public:
+ explicit COFFReader(const COFFObjectFile &O) : COFFObj(O) {}
+ Expected<std::unique_ptr<Object>> create() const;
+};
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OBJCOPY_COFF_READER_H
diff --git a/tools/llvm-objcopy/COFF/Writer.cpp b/tools/llvm-objcopy/COFF/Writer.cpp
new file mode 100644
index 000000000000..385d43b1bae5
--- /dev/null
+++ b/tools/llvm-objcopy/COFF/Writer.cpp
@@ -0,0 +1,337 @@
+//===- Writer.cpp ---------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Writer.h"
+#include "Object.h"
+#include "llvm-objcopy.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <cstddef>
+#include <cstdint>
+
+namespace llvm {
+namespace objcopy {
+namespace coff {
+
+using namespace object;
+using namespace COFF;
+
+Error COFFWriter::finalizeRelocTargets() {
+ for (Section &Sec : Obj.Sections) {
+ for (Relocation &R : Sec.Relocs) {
+ const Symbol *Sym = Obj.findSymbol(R.Target);
+ if (Sym == nullptr)
+ return make_error<StringError>("Relocation target " + R.TargetName +
+ " (" + Twine(R.Target) +
+ ") not found",
+ object_error::invalid_symbol_index);
+ R.Reloc.SymbolTableIndex = Sym->RawIndex;
+ }
+ }
+ return Error::success();
+}
+
+void COFFWriter::layoutSections() {
+ for (auto &S : Obj.Sections) {
+ if (S.Header.SizeOfRawData > 0)
+ S.Header.PointerToRawData = FileSize;
+ FileSize += S.Header.SizeOfRawData; // For executables, this is already
+ // aligned to FileAlignment.
+ S.Header.NumberOfRelocations = S.Relocs.size();
+ S.Header.PointerToRelocations =
+ S.Header.NumberOfRelocations > 0 ? FileSize : 0;
+ FileSize += S.Relocs.size() * sizeof(coff_relocation);
+ FileSize = alignTo(FileSize, FileAlignment);
+
+ if (S.Header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
+ SizeOfInitializedData += S.Header.SizeOfRawData;
+ }
+}
+
+size_t COFFWriter::finalizeStringTable() {
+ for (auto &S : Obj.Sections)
+ if (S.Name.size() > COFF::NameSize)
+ StrTabBuilder.add(S.Name);
+
+ for (const auto &S : Obj.getSymbols())
+ if (S.Name.size() > COFF::NameSize)
+ StrTabBuilder.add(S.Name);
+
+ StrTabBuilder.finalize();
+
+ for (auto &S : Obj.Sections) {
+ if (S.Name.size() > COFF::NameSize) {
+ snprintf(S.Header.Name, sizeof(S.Header.Name), "/%d",
+ (int)StrTabBuilder.getOffset(S.Name));
+ } else {
+ strncpy(S.Header.Name, S.Name.data(), COFF::NameSize);
+ }
+ }
+ for (auto &S : Obj.getMutableSymbols()) {
+ if (S.Name.size() > COFF::NameSize) {
+ S.Sym.Name.Offset.Zeroes = 0;
+ S.Sym.Name.Offset.Offset = StrTabBuilder.getOffset(S.Name);
+ } else {
+ strncpy(S.Sym.Name.ShortName, S.Name.data(), COFF::NameSize);
+ }
+ }
+ return StrTabBuilder.getSize();
+}
+
+template <class SymbolTy>
+std::pair<size_t, size_t> COFFWriter::finalizeSymbolTable() {
+ size_t SymTabSize = Obj.getSymbols().size() * sizeof(SymbolTy);
+ for (const auto &S : Obj.getSymbols())
+ SymTabSize += S.AuxData.size();
+ return std::make_pair(SymTabSize, sizeof(SymbolTy));
+}
+
+Error COFFWriter::finalize(bool IsBigObj) {
+ if (Error E = finalizeRelocTargets())
+ return E;
+
+ size_t SizeOfHeaders = 0;
+ FileAlignment = 1;
+ size_t PeHeaderSize = 0;
+ if (Obj.IsPE) {
+ Obj.DosHeader.AddressOfNewExeHeader =
+ sizeof(Obj.DosHeader) + Obj.DosStub.size();
+ SizeOfHeaders += Obj.DosHeader.AddressOfNewExeHeader + sizeof(PEMagic);
+
+ FileAlignment = Obj.PeHeader.FileAlignment;
+ Obj.PeHeader.NumberOfRvaAndSize = Obj.DataDirectories.size();
+
+ PeHeaderSize = Obj.Is64 ? sizeof(pe32plus_header) : sizeof(pe32_header);
+ SizeOfHeaders +=
+ PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size();
+ }
+ Obj.CoffFileHeader.NumberOfSections = Obj.Sections.size();
+ SizeOfHeaders +=
+ IsBigObj ? sizeof(coff_bigobj_file_header) : sizeof(coff_file_header);
+ SizeOfHeaders += sizeof(coff_section) * Obj.Sections.size();
+ SizeOfHeaders = alignTo(SizeOfHeaders, FileAlignment);
+
+ Obj.CoffFileHeader.SizeOfOptionalHeader =
+ PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size();
+
+ FileSize = SizeOfHeaders;
+ SizeOfInitializedData = 0;
+
+ layoutSections();
+
+ if (Obj.IsPE) {
+ Obj.PeHeader.SizeOfHeaders = SizeOfHeaders;
+ Obj.PeHeader.SizeOfInitializedData = SizeOfInitializedData;
+
+ if (!Obj.Sections.empty()) {
+ const Section &S = Obj.Sections.back();
+ Obj.PeHeader.SizeOfImage =
+ alignTo(S.Header.VirtualAddress + S.Header.VirtualSize,
+ Obj.PeHeader.SectionAlignment);
+ }
+
+ // If the PE header had a checksum, clear it, since it isn't valid
+ // any longer. (We don't calculate a new one.)
+ Obj.PeHeader.CheckSum = 0;
+ }
+
+ size_t StrTabSize = finalizeStringTable();
+ size_t SymTabSize, SymbolSize;
+ std::tie(SymTabSize, SymbolSize) = IsBigObj
+ ? finalizeSymbolTable<coff_symbol32>()
+ : finalizeSymbolTable<coff_symbol16>();
+
+ size_t PointerToSymbolTable = FileSize;
+ // StrTabSize <= 4 is the size of an empty string table, only consisting
+ // of the length field.
+ if (SymTabSize == 0 && StrTabSize <= 4 && Obj.IsPE) {
+ // For executables, don't point to the symbol table and skip writing
+ // the length field, if both the symbol and string tables are empty.
+ PointerToSymbolTable = 0;
+ StrTabSize = 0;
+ }
+
+ size_t NumRawSymbols = SymTabSize / SymbolSize;
+ Obj.CoffFileHeader.PointerToSymbolTable = PointerToSymbolTable;
+ Obj.CoffFileHeader.NumberOfSymbols = NumRawSymbols;
+ FileSize += SymTabSize + StrTabSize;
+ FileSize = alignTo(FileSize, FileAlignment);
+
+ return Error::success();
+}
+
+void COFFWriter::writeHeaders(bool IsBigObj) {
+ uint8_t *Ptr = Buf.getBufferStart();
+ if (Obj.IsPE) {
+ memcpy(Ptr, &Obj.DosHeader, sizeof(Obj.DosHeader));
+ Ptr += sizeof(Obj.DosHeader);
+ memcpy(Ptr, Obj.DosStub.data(), Obj.DosStub.size());
+ Ptr += Obj.DosStub.size();
+ memcpy(Ptr, PEMagic, sizeof(PEMagic));
+ Ptr += sizeof(PEMagic);
+ }
+ if (!IsBigObj) {
+ memcpy(Ptr, &Obj.CoffFileHeader, sizeof(Obj.CoffFileHeader));
+ Ptr += sizeof(Obj.CoffFileHeader);
+ } else {
+ // Generate a coff_bigobj_file_header, filling it in with the values
+ // from Obj.CoffFileHeader. All extra fields that don't exist in
+ // coff_file_header can be set to hardcoded values.
+ coff_bigobj_file_header BigObjHeader;
+ BigObjHeader.Sig1 = IMAGE_FILE_MACHINE_UNKNOWN;
+ BigObjHeader.Sig2 = 0xffff;
+ BigObjHeader.Version = BigObjHeader::MinBigObjectVersion;
+ BigObjHeader.Machine = Obj.CoffFileHeader.Machine;
+ BigObjHeader.TimeDateStamp = Obj.CoffFileHeader.TimeDateStamp;
+ memcpy(BigObjHeader.UUID, BigObjMagic, sizeof(BigObjMagic));
+ BigObjHeader.unused1 = 0;
+ BigObjHeader.unused2 = 0;
+ BigObjHeader.unused3 = 0;
+ BigObjHeader.unused4 = 0;
+ // The value in Obj.CoffFileHeader.NumberOfSections is truncated, thus
+ // get the original one instead.
+ BigObjHeader.NumberOfSections = Obj.Sections.size();
+ BigObjHeader.PointerToSymbolTable = Obj.CoffFileHeader.PointerToSymbolTable;
+ BigObjHeader.NumberOfSymbols = Obj.CoffFileHeader.NumberOfSymbols;
+
+ memcpy(Ptr, &BigObjHeader, sizeof(BigObjHeader));
+ Ptr += sizeof(BigObjHeader);
+ }
+ if (Obj.IsPE) {
+ if (Obj.Is64) {
+ memcpy(Ptr, &Obj.PeHeader, sizeof(Obj.PeHeader));
+ Ptr += sizeof(Obj.PeHeader);
+ } else {
+ pe32_header PeHeader;
+ copyPeHeader(PeHeader, Obj.PeHeader);
+ // The pe32plus_header (stored in Object) lacks the BaseOfData field.
+ PeHeader.BaseOfData = Obj.BaseOfData;
+
+ memcpy(Ptr, &PeHeader, sizeof(PeHeader));
+ Ptr += sizeof(PeHeader);
+ }
+ for (const auto &DD : Obj.DataDirectories) {
+ memcpy(Ptr, &DD, sizeof(DD));
+ Ptr += sizeof(DD);
+ }
+ }
+ for (const auto &S : Obj.Sections) {
+ memcpy(Ptr, &S.Header, sizeof(S.Header));
+ Ptr += sizeof(S.Header);
+ }
+}
+
+void COFFWriter::writeSections() {
+ for (const auto &S : Obj.Sections) {
+ uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData;
+ std::copy(S.Contents.begin(), S.Contents.end(), Ptr);
+
+ // For executable sections, pad the remainder of the raw data size with
+ // 0xcc, which is int3 on x86.
+ if ((S.Header.Characteristics & IMAGE_SCN_CNT_CODE) &&
+ S.Header.SizeOfRawData > S.Contents.size())
+ memset(Ptr + S.Contents.size(), 0xcc,
+ S.Header.SizeOfRawData - S.Contents.size());
+
+ Ptr += S.Header.SizeOfRawData;
+ for (const auto &R : S.Relocs) {
+ memcpy(Ptr, &R.Reloc, sizeof(R.Reloc));
+ Ptr += sizeof(R.Reloc);
+ }
+ }
+}
+
+template <class SymbolTy> void COFFWriter::writeSymbolStringTables() {
+ uint8_t *Ptr = Buf.getBufferStart() + Obj.CoffFileHeader.PointerToSymbolTable;
+ for (const auto &S : Obj.getSymbols()) {
+ // Convert symbols back to the right size, from coff_symbol32.
+ copySymbol<SymbolTy, coff_symbol32>(*reinterpret_cast<SymbolTy *>(Ptr),
+ S.Sym);
+ Ptr += sizeof(SymbolTy);
+ std::copy(S.AuxData.begin(), S.AuxData.end(), Ptr);
+ Ptr += S.AuxData.size();
+ }
+ if (StrTabBuilder.getSize() > 4 || !Obj.IsPE) {
+ // Always write a string table in object files, even an empty one.
+ StrTabBuilder.write(Ptr);
+ Ptr += StrTabBuilder.getSize();
+ }
+}
+
+Error COFFWriter::write(bool IsBigObj) {
+ if (Error E = finalize(IsBigObj))
+ return E;
+
+ Buf.allocate(FileSize);
+
+ writeHeaders(IsBigObj);
+ writeSections();
+ if (IsBigObj)
+ writeSymbolStringTables<coff_symbol32>();
+ else
+ writeSymbolStringTables<coff_symbol16>();
+
+ if (Obj.IsPE)
+ if (Error E = patchDebugDirectory())
+ return E;
+
+ return Buf.commit();
+}
+
+// Locate which sections contain the debug directories, iterate over all
+// the debug_directory structs in there, and set the PointerToRawData field
+// in all of them, according to their new physical location in the file.
+Error COFFWriter::patchDebugDirectory() {
+ if (Obj.DataDirectories.size() < DEBUG_DIRECTORY)
+ return Error::success();
+ const data_directory *Dir = &Obj.DataDirectories[DEBUG_DIRECTORY];
+ if (Dir->Size <= 0)
+ return Error::success();
+ for (const auto &S : Obj.Sections) {
+ if (Dir->RelativeVirtualAddress >= S.Header.VirtualAddress &&
+ Dir->RelativeVirtualAddress <
+ S.Header.VirtualAddress + S.Header.SizeOfRawData) {
+ if (Dir->RelativeVirtualAddress + Dir->Size >
+ S.Header.VirtualAddress + S.Header.SizeOfRawData)
+ return make_error<StringError>(
+ "Debug directory extends past end of section",
+ object_error::parse_failed);
+
+ size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress;
+ uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData + Offset;
+ uint8_t *End = Ptr + Dir->Size;
+ while (Ptr < End) {
+ debug_directory *Debug = reinterpret_cast<debug_directory *>(Ptr);
+ Debug->PointerToRawData =
+ S.Header.PointerToRawData + Offset + sizeof(debug_directory);
+ Ptr += sizeof(debug_directory) + Debug->SizeOfData;
+ Offset += sizeof(debug_directory) + Debug->SizeOfData;
+ }
+ // Debug directory found and patched, all done.
+ return Error::success();
+ }
+ }
+ return make_error<StringError>("Debug directory not found",
+ object_error::parse_failed);
+}
+
+Error COFFWriter::write() {
+ bool IsBigObj = Obj.Sections.size() > MaxNumberOfSections16;
+ if (IsBigObj && Obj.IsPE)
+ return make_error<StringError>("Too many sections for executable",
+ object_error::parse_failed);
+ return write(IsBigObj);
+}
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/tools/llvm-objcopy/COFF/Writer.h b/tools/llvm-objcopy/COFF/Writer.h
new file mode 100644
index 000000000000..ab66e0cc1134
--- /dev/null
+++ b/tools/llvm-objcopy/COFF/Writer.h
@@ -0,0 +1,61 @@
+//===- Writer.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_COFF_WRITER_H
+#define LLVM_TOOLS_OBJCOPY_COFF_WRITER_H
+
+#include "Buffer.h"
+#include "llvm/MC/StringTableBuilder.h"
+#include "llvm/Support/Error.h"
+#include <cstddef>
+#include <utility>
+
+namespace llvm {
+namespace objcopy {
+namespace coff {
+
+struct Object;
+
+class COFFWriter {
+ Object &Obj;
+ Buffer &Buf;
+
+ size_t FileSize;
+ size_t FileAlignment;
+ size_t SizeOfInitializedData;
+ StringTableBuilder StrTabBuilder;
+
+ Error finalizeRelocTargets();
+ void layoutSections();
+ size_t finalizeStringTable();
+ template <class SymbolTy> std::pair<size_t, size_t> finalizeSymbolTable();
+
+ Error finalize(bool IsBigObj);
+
+ void writeHeaders(bool IsBigObj);
+ void writeSections();
+ template <class SymbolTy> void writeSymbolStringTables();
+
+ Error write(bool IsBigObj);
+
+ Error patchDebugDirectory();
+
+public:
+ virtual ~COFFWriter() {}
+ Error write();
+
+ COFFWriter(Object &Obj, Buffer &Buf)
+ : Obj(Obj), Buf(Buf), StrTabBuilder(StringTableBuilder::WinCOFF) {}
+};
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OBJCOPY_COFF_WRITER_H
diff --git a/tools/llvm-objcopy/CopyConfig.cpp b/tools/llvm-objcopy/CopyConfig.cpp
new file mode 100644
index 000000000000..3737f571ae61
--- /dev/null
+++ b/tools/llvm-objcopy/CopyConfig.cpp
@@ -0,0 +1,474 @@
+//===- CopyConfig.cpp -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CopyConfig.h"
+#include "llvm-objcopy.h"
+
+#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/ELFTypes.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Compression.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <memory>
+#include <string>
+
+namespace llvm {
+namespace objcopy {
+
+namespace {
+enum ObjcopyID {
+ OBJCOPY_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OBJCOPY_##ID,
+#include "ObjcopyOpts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const OBJCOPY_##NAME[] = VALUE;
+#include "ObjcopyOpts.inc"
+#undef PREFIX
+
+static const opt::OptTable::Info ObjcopyInfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {OBJCOPY_##PREFIX, \
+ NAME, \
+ HELPTEXT, \
+ METAVAR, \
+ OBJCOPY_##ID, \
+ opt::Option::KIND##Class, \
+ PARAM, \
+ FLAGS, \
+ OBJCOPY_##GROUP, \
+ OBJCOPY_##ALIAS, \
+ ALIASARGS, \
+ VALUES},
+#include "ObjcopyOpts.inc"
+#undef OPTION
+};
+
+class ObjcopyOptTable : public opt::OptTable {
+public:
+ ObjcopyOptTable() : OptTable(ObjcopyInfoTable) {}
+};
+
+enum StripID {
+ STRIP_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ STRIP_##ID,
+#include "StripOpts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const STRIP_##NAME[] = VALUE;
+#include "StripOpts.inc"
+#undef PREFIX
+
+static const opt::OptTable::Info StripInfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {STRIP_##PREFIX, NAME, HELPTEXT, \
+ METAVAR, STRIP_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, STRIP_##GROUP, \
+ STRIP_##ALIAS, ALIASARGS, VALUES},
+#include "StripOpts.inc"
+#undef OPTION
+};
+
+class StripOptTable : public opt::OptTable {
+public:
+ StripOptTable() : OptTable(StripInfoTable) {}
+};
+
+enum SectionFlag {
+ SecNone = 0,
+ SecAlloc = 1 << 0,
+ SecLoad = 1 << 1,
+ SecNoload = 1 << 2,
+ SecReadonly = 1 << 3,
+ SecDebug = 1 << 4,
+ SecCode = 1 << 5,
+ SecData = 1 << 6,
+ SecRom = 1 << 7,
+ SecMerge = 1 << 8,
+ SecStrings = 1 << 9,
+ SecContents = 1 << 10,
+ SecShare = 1 << 11,
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ SecShare)
+};
+
+} // namespace
+
+static SectionFlag parseSectionRenameFlag(StringRef SectionName) {
+ return llvm::StringSwitch<SectionFlag>(SectionName)
+ .Case("alloc", SectionFlag::SecAlloc)
+ .Case("load", SectionFlag::SecLoad)
+ .Case("noload", SectionFlag::SecNoload)
+ .Case("readonly", SectionFlag::SecReadonly)
+ .Case("debug", SectionFlag::SecDebug)
+ .Case("code", SectionFlag::SecCode)
+ .Case("data", SectionFlag::SecData)
+ .Case("rom", SectionFlag::SecRom)
+ .Case("merge", SectionFlag::SecMerge)
+ .Case("strings", SectionFlag::SecStrings)
+ .Case("contents", SectionFlag::SecContents)
+ .Case("share", SectionFlag::SecShare)
+ .Default(SectionFlag::SecNone);
+}
+
+static SectionRename parseRenameSectionValue(StringRef FlagValue) {
+ if (!FlagValue.contains('='))
+ error("Bad format for --rename-section: missing '='");
+
+ // Initial split: ".foo" = ".bar,f1,f2,..."
+ auto Old2New = FlagValue.split('=');
+ SectionRename SR;
+ SR.OriginalName = Old2New.first;
+
+ // Flags split: ".bar" "f1" "f2" ...
+ SmallVector<StringRef, 6> NameAndFlags;
+ Old2New.second.split(NameAndFlags, ',');
+ SR.NewName = NameAndFlags[0];
+
+ if (NameAndFlags.size() > 1) {
+ SectionFlag Flags = SectionFlag::SecNone;
+ for (size_t I = 1, Size = NameAndFlags.size(); I < Size; ++I) {
+ SectionFlag Flag = parseSectionRenameFlag(NameAndFlags[I]);
+ if (Flag == SectionFlag::SecNone)
+ error("Unrecognized section flag '" + NameAndFlags[I] +
+ "'. Flags supported for GNU compatibility: alloc, load, noload, "
+ "readonly, debug, code, data, rom, share, contents, merge, "
+ "strings.");
+ Flags |= Flag;
+ }
+
+ SR.NewFlags = 0;
+ if (Flags & SectionFlag::SecAlloc)
+ *SR.NewFlags |= ELF::SHF_ALLOC;
+ if (!(Flags & SectionFlag::SecReadonly))
+ *SR.NewFlags |= ELF::SHF_WRITE;
+ if (Flags & SectionFlag::SecCode)
+ *SR.NewFlags |= ELF::SHF_EXECINSTR;
+ if (Flags & SectionFlag::SecMerge)
+ *SR.NewFlags |= ELF::SHF_MERGE;
+ if (Flags & SectionFlag::SecStrings)
+ *SR.NewFlags |= ELF::SHF_STRINGS;
+ }
+
+ return SR;
+}
+
+static const StringMap<MachineInfo> ArchMap{
+ // Name, {EMachine, 64bit, LittleEndian}
+ {"aarch64", {ELF::EM_AARCH64, true, true}},
+ {"arm", {ELF::EM_ARM, false, true}},
+ {"i386", {ELF::EM_386, false, true}},
+ {"i386:x86-64", {ELF::EM_X86_64, true, true}},
+ {"powerpc:common64", {ELF::EM_PPC64, true, true}},
+ {"sparc", {ELF::EM_SPARC, false, true}},
+ {"x86-64", {ELF::EM_X86_64, true, true}},
+};
+
+static const MachineInfo &getMachineInfo(StringRef Arch) {
+ auto Iter = ArchMap.find(Arch);
+ if (Iter == std::end(ArchMap))
+ error("Invalid architecture: '" + Arch + "'");
+ return Iter->getValue();
+}
+
+static const StringMap<MachineInfo> OutputFormatMap{
+ // Name, {EMachine, 64bit, LittleEndian}
+ {"elf32-i386", {ELF::EM_386, false, true}},
+ {"elf32-powerpcle", {ELF::EM_PPC, false, true}},
+ {"elf32-x86-64", {ELF::EM_X86_64, false, true}},
+ {"elf64-powerpcle", {ELF::EM_PPC64, true, true}},
+ {"elf64-x86-64", {ELF::EM_X86_64, true, true}},
+};
+
+static const MachineInfo &getOutputFormatMachineInfo(StringRef Format) {
+ auto Iter = OutputFormatMap.find(Format);
+ if (Iter == std::end(OutputFormatMap))
+ error("Invalid output format: '" + Format + "'");
+ return Iter->getValue();
+}
+
+static void addGlobalSymbolsFromFile(std::vector<std::string> &Symbols,
+ StringRef Filename) {
+ SmallVector<StringRef, 16> Lines;
+ auto BufOrErr = MemoryBuffer::getFile(Filename);
+ if (!BufOrErr)
+ reportError(Filename, BufOrErr.getError());
+
+ BufOrErr.get()->getBuffer().split(Lines, '\n');
+ for (StringRef Line : Lines) {
+ // Ignore everything after '#', trim whitespace, and only add the symbol if
+ // it's not empty.
+ auto TrimmedLine = Line.split('#').first.trim();
+ if (!TrimmedLine.empty())
+ Symbols.push_back(TrimmedLine.str());
+ }
+}
+
+// ParseObjcopyOptions returns the config and sets the input arguments. If a
+// help flag is set then ParseObjcopyOptions will print the help messege and
+// exit.
+DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
+ ObjcopyOptTable T;
+ unsigned MissingArgumentIndex, MissingArgumentCount;
+ llvm::opt::InputArgList InputArgs =
+ T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+
+ if (InputArgs.size() == 0) {
+ T.PrintHelp(errs(), "llvm-objcopy input [output]", "objcopy tool");
+ exit(1);
+ }
+
+ if (InputArgs.hasArg(OBJCOPY_help)) {
+ T.PrintHelp(outs(), "llvm-objcopy input [output]", "objcopy tool");
+ exit(0);
+ }
+
+ if (InputArgs.hasArg(OBJCOPY_version)) {
+ outs() << "llvm-objcopy, compatible with GNU objcopy\n";
+ cl::PrintVersionMessage();
+ exit(0);
+ }
+
+ SmallVector<const char *, 2> Positional;
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_UNKNOWN))
+ error("unknown argument '" + Arg->getAsString(InputArgs) + "'");
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT))
+ Positional.push_back(Arg->getValue());
+
+ if (Positional.empty())
+ error("No input file specified");
+
+ if (Positional.size() > 2)
+ error("Too many positional arguments");
+
+ CopyConfig Config;
+ Config.InputFilename = Positional[0];
+ Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1];
+ if (InputArgs.hasArg(OBJCOPY_target) &&
+ (InputArgs.hasArg(OBJCOPY_input_target) ||
+ InputArgs.hasArg(OBJCOPY_output_target)))
+ error("--target cannot be used with --input-target or --output-target");
+
+ if (InputArgs.hasArg(OBJCOPY_target)) {
+ Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_target);
+ Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target);
+ } else {
+ Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target);
+ Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target);
+ }
+ if (Config.InputFormat == "binary") {
+ auto BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture);
+ if (BinaryArch.empty())
+ error("Specified binary input without specifiying an architecture");
+ Config.BinaryArch = getMachineInfo(BinaryArch);
+ }
+ if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary")
+ Config.OutputArch = getOutputFormatMachineInfo(Config.OutputFormat);
+
+ if (auto Arg = InputArgs.getLastArg(OBJCOPY_compress_debug_sections,
+ OBJCOPY_compress_debug_sections_eq)) {
+ Config.CompressionType = DebugCompressionType::Z;
+
+ if (Arg->getOption().getID() == OBJCOPY_compress_debug_sections_eq) {
+ Config.CompressionType =
+ StringSwitch<DebugCompressionType>(
+ InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq))
+ .Case("zlib-gnu", DebugCompressionType::GNU)
+ .Case("zlib", DebugCompressionType::Z)
+ .Default(DebugCompressionType::None);
+ if (Config.CompressionType == DebugCompressionType::None)
+ error("Invalid or unsupported --compress-debug-sections format: " +
+ InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq));
+ if (!zlib::isAvailable())
+ error("LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress.");
+ }
+ }
+
+ Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink);
+ Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir);
+ if (InputArgs.hasArg(OBJCOPY_build_id_link_input))
+ Config.BuildIdLinkInput =
+ InputArgs.getLastArgValue(OBJCOPY_build_id_link_input);
+ if (InputArgs.hasArg(OBJCOPY_build_id_link_output))
+ Config.BuildIdLinkOutput =
+ InputArgs.getLastArgValue(OBJCOPY_build_id_link_output);
+ Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo);
+ Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols);
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) {
+ if (!StringRef(Arg->getValue()).contains('='))
+ error("Bad format for --redefine-sym");
+ auto Old2New = StringRef(Arg->getValue()).split('=');
+ if (!Config.SymbolsToRename.insert(Old2New).second)
+ error("Multiple redefinition of symbol " + Old2New.first);
+ }
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_rename_section)) {
+ SectionRename SR = parseRenameSectionValue(StringRef(Arg->getValue()));
+ if (!Config.SectionsToRename.try_emplace(SR.OriginalName, SR).second)
+ error("Multiple renames of section " + SR.OriginalName);
+ }
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section))
+ Config.ToRemove.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_keep_section))
+ Config.KeepSection.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_only_section))
+ Config.OnlySection.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_add_section))
+ Config.AddSection.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section))
+ Config.DumpSection.push_back(Arg->getValue());
+ Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all);
+ Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu);
+ Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug);
+ Config.StripDWO = InputArgs.hasArg(OBJCOPY_strip_dwo);
+ Config.StripSections = InputArgs.hasArg(OBJCOPY_strip_sections);
+ Config.StripNonAlloc = InputArgs.hasArg(OBJCOPY_strip_non_alloc);
+ Config.StripUnneeded = InputArgs.hasArg(OBJCOPY_strip_unneeded);
+ Config.ExtractDWO = InputArgs.hasArg(OBJCOPY_extract_dwo);
+ Config.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden);
+ Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken);
+ Config.DiscardAll = InputArgs.hasArg(OBJCOPY_discard_all);
+ Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug);
+ Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols);
+ Config.DecompressDebugSections =
+ InputArgs.hasArg(OBJCOPY_decompress_debug_sections);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol))
+ Config.SymbolsToLocalize.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol))
+ Config.SymbolsToKeepGlobal.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols))
+ addGlobalSymbolsFromFile(Config.SymbolsToKeepGlobal, Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol))
+ Config.SymbolsToGlobalize.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol))
+ Config.SymbolsToWeaken.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol))
+ Config.SymbolsToRemove.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol))
+ Config.SymbolsToKeep.push_back(Arg->getValue());
+
+ Config.DeterministicArchives = InputArgs.hasFlag(
+ OBJCOPY_enable_deterministic_archives,
+ OBJCOPY_disable_deterministic_archives, /*default=*/true);
+
+ Config.PreserveDates = InputArgs.hasArg(OBJCOPY_preserve_dates);
+
+ if (Config.DecompressDebugSections &&
+ Config.CompressionType != DebugCompressionType::None) {
+ error("Cannot specify --compress-debug-sections at the same time as "
+ "--decompress-debug-sections at the same time");
+ }
+
+ if (Config.DecompressDebugSections && !zlib::isAvailable())
+ error("LLVM was not compiled with LLVM_ENABLE_ZLIB: cannot decompress.");
+
+ DriverConfig DC;
+ DC.CopyConfigs.push_back(std::move(Config));
+ return DC;
+}
+
+// ParseStripOptions returns the config and sets the input arguments. If a
+// help flag is set then ParseStripOptions will print the help messege and
+// exit.
+DriverConfig parseStripOptions(ArrayRef<const char *> ArgsArr) {
+ StripOptTable T;
+ unsigned MissingArgumentIndex, MissingArgumentCount;
+ llvm::opt::InputArgList InputArgs =
+ T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+
+ if (InputArgs.size() == 0) {
+ T.PrintHelp(errs(), "llvm-strip [options] file...", "strip tool");
+ exit(1);
+ }
+
+ if (InputArgs.hasArg(STRIP_help)) {
+ T.PrintHelp(outs(), "llvm-strip [options] file...", "strip tool");
+ exit(0);
+ }
+
+ if (InputArgs.hasArg(STRIP_version)) {
+ outs() << "llvm-strip, compatible with GNU strip\n";
+ cl::PrintVersionMessage();
+ exit(0);
+ }
+
+ SmallVector<const char *, 2> Positional;
+ for (auto Arg : InputArgs.filtered(STRIP_UNKNOWN))
+ error("unknown argument '" + Arg->getAsString(InputArgs) + "'");
+ for (auto Arg : InputArgs.filtered(STRIP_INPUT))
+ Positional.push_back(Arg->getValue());
+
+ if (Positional.empty())
+ error("No input file specified");
+
+ if (Positional.size() > 1 && InputArgs.hasArg(STRIP_output))
+ error("Multiple input files cannot be used in combination with -o");
+
+ CopyConfig Config;
+ Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug);
+
+ Config.DiscardAll = InputArgs.hasArg(STRIP_discard_all);
+ Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded);
+ Config.StripAll = InputArgs.hasArg(STRIP_strip_all);
+ Config.StripAllGNU = InputArgs.hasArg(STRIP_strip_all_gnu);
+
+ if (!Config.StripDebug && !Config.StripUnneeded && !Config.DiscardAll &&
+ !Config.StripAllGNU)
+ Config.StripAll = true;
+
+ for (auto Arg : InputArgs.filtered(STRIP_keep_section))
+ Config.KeepSection.push_back(Arg->getValue());
+
+ for (auto Arg : InputArgs.filtered(STRIP_remove_section))
+ Config.ToRemove.push_back(Arg->getValue());
+
+ for (auto Arg : InputArgs.filtered(STRIP_keep_symbol))
+ Config.SymbolsToKeep.push_back(Arg->getValue());
+
+ Config.DeterministicArchives =
+ InputArgs.hasFlag(STRIP_enable_deterministic_archives,
+ STRIP_disable_deterministic_archives, /*default=*/true);
+
+ Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates);
+
+ DriverConfig DC;
+ if (Positional.size() == 1) {
+ Config.InputFilename = Positional[0];
+ Config.OutputFilename =
+ InputArgs.getLastArgValue(STRIP_output, Positional[0]);
+ DC.CopyConfigs.push_back(std::move(Config));
+ } else {
+ for (const char *Filename : Positional) {
+ Config.InputFilename = Filename;
+ Config.OutputFilename = Filename;
+ DC.CopyConfigs.push_back(Config);
+ }
+ }
+
+ return DC;
+}
+
+} // namespace objcopy
+} // namespace llvm
diff --git a/tools/llvm-objcopy/CopyConfig.h b/tools/llvm-objcopy/CopyConfig.h
new file mode 100644
index 000000000000..71a2423ae1c8
--- /dev/null
+++ b/tools/llvm-objcopy/CopyConfig.h
@@ -0,0 +1,119 @@
+//===- CopyConfig.h -------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+// Necessary for llvm::DebugCompressionType::None
+#include "llvm/Target/TargetOptions.h"
+#include <string>
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+
+// This type keeps track of the machine info for various architectures. This
+// lets us map architecture names to ELF types and the e_machine value of the
+// ELF file.
+struct MachineInfo {
+ uint16_t EMachine;
+ bool Is64Bit;
+ bool IsLittleEndian;
+};
+
+struct SectionRename {
+ StringRef OriginalName;
+ StringRef NewName;
+ Optional<uint64_t> NewFlags;
+};
+
+// Configuration for copying/stripping a single file.
+struct CopyConfig {
+ // Main input/output options
+ StringRef InputFilename;
+ StringRef InputFormat;
+ StringRef OutputFilename;
+ StringRef OutputFormat;
+
+ // Only applicable for --input-format=binary
+ MachineInfo BinaryArch;
+ // Only applicable when --output-format!=binary (e.g. elf64-x86-64).
+ Optional<MachineInfo> OutputArch;
+
+ // Advanced options
+ StringRef AddGnuDebugLink;
+ StringRef BuildIdLinkDir;
+ Optional<StringRef> BuildIdLinkInput;
+ Optional<StringRef> BuildIdLinkOutput;
+ StringRef SplitDWO;
+ StringRef SymbolsPrefix;
+
+ // Repeated options
+ std::vector<StringRef> AddSection;
+ std::vector<StringRef> DumpSection;
+ std::vector<StringRef> KeepSection;
+ std::vector<StringRef> OnlySection;
+ std::vector<StringRef> SymbolsToGlobalize;
+ std::vector<StringRef> SymbolsToKeep;
+ std::vector<StringRef> SymbolsToLocalize;
+ std::vector<StringRef> SymbolsToRemove;
+ std::vector<StringRef> SymbolsToWeaken;
+ std::vector<StringRef> ToRemove;
+ std::vector<std::string> SymbolsToKeepGlobal;
+
+ // Map options
+ StringMap<SectionRename> SectionsToRename;
+ StringMap<StringRef> SymbolsToRename;
+
+ // Boolean options
+ bool DeterministicArchives = true;
+ bool DiscardAll = false;
+ bool ExtractDWO = false;
+ bool KeepFileSymbols = false;
+ bool LocalizeHidden = false;
+ bool OnlyKeepDebug = false;
+ bool PreserveDates = false;
+ bool StripAll = false;
+ bool StripAllGNU = false;
+ bool StripDWO = false;
+ bool StripDebug = false;
+ bool StripNonAlloc = false;
+ bool StripSections = false;
+ bool StripUnneeded = false;
+ bool Weaken = false;
+ bool DecompressDebugSections = false;
+ DebugCompressionType CompressionType = DebugCompressionType::None;
+};
+
+// Configuration for the overall invocation of this tool. When invoked as
+// objcopy, will always contain exactly one CopyConfig. When invoked as strip,
+// will contain one or more CopyConfigs.
+struct DriverConfig {
+ SmallVector<CopyConfig, 1> CopyConfigs;
+};
+
+// ParseObjcopyOptions returns the config and sets the input arguments. If a
+// help flag is set then ParseObjcopyOptions will print the help messege and
+// exit.
+DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr);
+
+// ParseStripOptions returns the config and sets the input arguments. If a
+// help flag is set then ParseStripOptions will print the help messege and
+// exit.
+DriverConfig parseStripOptions(ArrayRef<const char *> ArgsArr);
+
+} // namespace objcopy
+} // namespace llvm
+
+#endif
diff --git a/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
new file mode 100644
index 000000000000..f5ab8e708267
--- /dev/null
+++ b/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
@@ -0,0 +1,584 @@
+//===- ELFObjcopy.cpp -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ELFObjcopy.h"
+#include "Buffer.h"
+#include "CopyConfig.h"
+#include "Object.h"
+#include "llvm-objcopy.h"
+
+#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/ELFTypes.h"
+#include "llvm/Object/Error.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compression.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/Memory.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <cstdlib>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <utility>
+
+namespace llvm {
+namespace objcopy {
+namespace elf {
+
+using namespace object;
+using namespace ELF;
+using SectionPred = std::function<bool(const SectionBase &Sec)>;
+
+static bool isDebugSection(const SectionBase &Sec) {
+ return StringRef(Sec.Name).startswith(".debug") ||
+ StringRef(Sec.Name).startswith(".zdebug") || Sec.Name == ".gdb_index";
+}
+
+static bool isDWOSection(const SectionBase &Sec) {
+ return StringRef(Sec.Name).endswith(".dwo");
+}
+
+static bool onlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) {
+ // We can't remove the section header string table.
+ if (&Sec == Obj.SectionNames)
+ return false;
+ // Short of keeping the string table we want to keep everything that is a DWO
+ // section and remove everything else.
+ return !isDWOSection(Sec);
+}
+
+static ElfType getOutputElfType(const Binary &Bin) {
+ // Infer output ELF type from the input ELF object
+ if (isa<ELFObjectFile<ELF32LE>>(Bin))
+ return ELFT_ELF32LE;
+ if (isa<ELFObjectFile<ELF64LE>>(Bin))
+ return ELFT_ELF64LE;
+ if (isa<ELFObjectFile<ELF32BE>>(Bin))
+ return ELFT_ELF32BE;
+ if (isa<ELFObjectFile<ELF64BE>>(Bin))
+ return ELFT_ELF64BE;
+ llvm_unreachable("Invalid ELFType");
+}
+
+static ElfType getOutputElfType(const MachineInfo &MI) {
+ // Infer output ELF type from the binary arch specified
+ if (MI.Is64Bit)
+ return MI.IsLittleEndian ? ELFT_ELF64LE : ELFT_ELF64BE;
+ else
+ return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE;
+}
+
+static std::unique_ptr<Writer> createWriter(const CopyConfig &Config,
+ Object &Obj, Buffer &Buf,
+ ElfType OutputElfType) {
+ if (Config.OutputFormat == "binary") {
+ return llvm::make_unique<BinaryWriter>(Obj, Buf);
+ }
+ // Depending on the initial ELFT and OutputFormat we need a different Writer.
+ switch (OutputElfType) {
+ case ELFT_ELF32LE:
+ return llvm::make_unique<ELFWriter<ELF32LE>>(Obj, Buf,
+ !Config.StripSections);
+ case ELFT_ELF64LE:
+ return llvm::make_unique<ELFWriter<ELF64LE>>(Obj, Buf,
+ !Config.StripSections);
+ case ELFT_ELF32BE:
+ return llvm::make_unique<ELFWriter<ELF32BE>>(Obj, Buf,
+ !Config.StripSections);
+ case ELFT_ELF64BE:
+ return llvm::make_unique<ELFWriter<ELF64BE>>(Obj, Buf,
+ !Config.StripSections);
+ }
+ llvm_unreachable("Invalid output format");
+}
+
+template <class ELFT>
+static Expected<ArrayRef<uint8_t>>
+findBuildID(const object::ELFFile<ELFT> &In) {
+ for (const auto &Phdr : unwrapOrError(In.program_headers())) {
+ if (Phdr.p_type != PT_NOTE)
+ continue;
+ Error Err = Error::success();
+ for (const auto &Note : In.notes(Phdr, Err))
+ if (Note.getType() == NT_GNU_BUILD_ID && Note.getName() == ELF_NOTE_GNU)
+ return Note.getDesc();
+ if (Err)
+ return std::move(Err);
+ }
+ return createStringError(llvm::errc::invalid_argument,
+ "Could not find build ID.");
+}
+
+static Expected<ArrayRef<uint8_t>>
+findBuildID(const object::ELFObjectFileBase &In) {
+ if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(&In))
+ return findBuildID(*O->getELFFile());
+ else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(&In))
+ return findBuildID(*O->getELFFile());
+ else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(&In))
+ return findBuildID(*O->getELFFile());
+ else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(&In))
+ return findBuildID(*O->getELFFile());
+
+ llvm_unreachable("Bad file format");
+}
+
+static void linkToBuildIdDir(const CopyConfig &Config, StringRef ToLink,
+ StringRef Suffix, ArrayRef<uint8_t> BuildIdBytes) {
+ SmallString<128> Path = Config.BuildIdLinkDir;
+ sys::path::append(Path, llvm::toHex(BuildIdBytes[0], /*LowerCase*/ true));
+ if (auto EC = sys::fs::create_directories(Path))
+ error("cannot create build ID link directory " + Path + ": " +
+ EC.message());
+
+ sys::path::append(Path,
+ llvm::toHex(BuildIdBytes.slice(1), /*LowerCase*/ true));
+ Path += Suffix;
+ if (auto EC = sys::fs::create_hard_link(ToLink, Path)) {
+ // Hard linking failed, try to remove the file first if it exists.
+ if (sys::fs::exists(Path))
+ sys::fs::remove(Path);
+ EC = sys::fs::create_hard_link(ToLink, Path);
+ if (EC)
+ error("cannot link " + ToLink + " to " + Path + ": " + EC.message());
+ }
+}
+
+static void splitDWOToFile(const CopyConfig &Config, const Reader &Reader,
+ StringRef File, ElfType OutputElfType) {
+ auto DWOFile = Reader.create();
+ DWOFile->removeSections(
+ [&](const SectionBase &Sec) { return onlyKeepDWOPred(*DWOFile, Sec); });
+ if (Config.OutputArch)
+ DWOFile->Machine = Config.OutputArch.getValue().EMachine;
+ FileBuffer FB(File);
+ auto Writer = createWriter(Config, *DWOFile, FB, OutputElfType);
+ Writer->finalize();
+ Writer->write();
+}
+
+static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
+ Object &Obj) {
+ for (auto &Sec : Obj.sections()) {
+ if (Sec.Name == SecName) {
+ if (Sec.OriginalData.empty())
+ return make_error<StringError>("Can't dump section \"" + SecName +
+ "\": it has no contents",
+ object_error::parse_failed);
+ Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+ FileOutputBuffer::create(Filename, Sec.OriginalData.size());
+ if (!BufferOrErr)
+ return BufferOrErr.takeError();
+ std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);
+ std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(),
+ Buf->getBufferStart());
+ if (Error E = Buf->commit())
+ return E;
+ return Error::success();
+ }
+ }
+ return make_error<StringError>("Section not found",
+ object_error::parse_failed);
+}
+
+static bool isCompressed(const SectionBase &Section) {
+ const char *Magic = "ZLIB";
+ return StringRef(Section.Name).startswith(".zdebug") ||
+ (Section.OriginalData.size() > strlen(Magic) &&
+ !strncmp(reinterpret_cast<const char *>(Section.OriginalData.data()),
+ Magic, strlen(Magic))) ||
+ (Section.Flags & ELF::SHF_COMPRESSED);
+}
+
+static bool isCompressable(const SectionBase &Section) {
+ return !isCompressed(Section) && isDebugSection(Section) &&
+ Section.Name != ".gdb_index";
+}
+
+static void replaceDebugSections(
+ const CopyConfig &Config, Object &Obj, SectionPred &RemovePred,
+ function_ref<bool(const SectionBase &)> shouldReplace,
+ function_ref<SectionBase *(const SectionBase *)> addSection) {
+ SmallVector<SectionBase *, 13> ToReplace;
+ SmallVector<RelocationSection *, 13> RelocationSections;
+ for (auto &Sec : Obj.sections()) {
+ if (RelocationSection *R = dyn_cast<RelocationSection>(&Sec)) {
+ if (shouldReplace(*R->getSection()))
+ RelocationSections.push_back(R);
+ continue;
+ }
+
+ if (shouldReplace(Sec))
+ ToReplace.push_back(&Sec);
+ }
+
+ for (SectionBase *S : ToReplace) {
+ SectionBase *NewSection = addSection(S);
+
+ for (RelocationSection *RS : RelocationSections) {
+ if (RS->getSection() == S)
+ RS->setSection(NewSection);
+ }
+ }
+
+ RemovePred = [shouldReplace, RemovePred](const SectionBase &Sec) {
+ return shouldReplace(Sec) || RemovePred(Sec);
+ };
+}
+
+// This function handles the high level operations of GNU objcopy including
+// handling command line options. It's important to outline certain properties
+// we expect to hold of the command line operations. Any operation that "keeps"
+// should keep regardless of a remove. Additionally any removal should respect
+// any previous removals. Lastly whether or not something is removed shouldn't
+// depend a) on the order the options occur in or b) on some opaque priority
+// system. The only priority is that keeps/copies overrule removes.
+static void handleArgs(const CopyConfig &Config, Object &Obj,
+ const Reader &Reader, ElfType OutputElfType) {
+
+ if (!Config.SplitDWO.empty()) {
+ splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType);
+ }
+ if (Config.OutputArch)
+ Obj.Machine = Config.OutputArch.getValue().EMachine;
+
+ // TODO: update or remove symbols only if there is an option that affects
+ // them.
+ if (Obj.SymbolTable) {
+ Obj.SymbolTable->updateSymbols([&](Symbol &Sym) {
+ if (!Sym.isCommon() &&
+ ((Config.LocalizeHidden &&
+ (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) ||
+ is_contained(Config.SymbolsToLocalize, Sym.Name)))
+ Sym.Binding = STB_LOCAL;
+
+ // Note: these two globalize flags have very similar names but different
+ // meanings:
+ //
+ // --globalize-symbol: promote a symbol to global
+ // --keep-global-symbol: all symbols except for these should be made local
+ //
+ // If --globalize-symbol is specified for a given symbol, it will be
+ // global in the output file even if it is not included via
+ // --keep-global-symbol. Because of that, make sure to check
+ // --globalize-symbol second.
+ if (!Config.SymbolsToKeepGlobal.empty() &&
+ !is_contained(Config.SymbolsToKeepGlobal, Sym.Name) &&
+ Sym.getShndx() != SHN_UNDEF)
+ Sym.Binding = STB_LOCAL;
+
+ if (is_contained(Config.SymbolsToGlobalize, Sym.Name) &&
+ Sym.getShndx() != SHN_UNDEF)
+ Sym.Binding = STB_GLOBAL;
+
+ if (is_contained(Config.SymbolsToWeaken, Sym.Name) &&
+ Sym.Binding == STB_GLOBAL)
+ Sym.Binding = STB_WEAK;
+
+ if (Config.Weaken && Sym.Binding == STB_GLOBAL &&
+ Sym.getShndx() != SHN_UNDEF)
+ Sym.Binding = STB_WEAK;
+
+ const auto I = Config.SymbolsToRename.find(Sym.Name);
+ if (I != Config.SymbolsToRename.end())
+ Sym.Name = I->getValue();
+
+ if (!Config.SymbolsPrefix.empty() && Sym.Type != STT_SECTION)
+ Sym.Name = (Config.SymbolsPrefix + Sym.Name).str();
+ });
+
+ // The purpose of this loop is to mark symbols referenced by sections
+ // (like GroupSection or RelocationSection). This way, we know which
+ // symbols are still 'needed' and which are not.
+ if (Config.StripUnneeded) {
+ for (auto &Section : Obj.sections())
+ Section.markSymbols();
+ }
+
+ Obj.removeSymbols([&](const Symbol &Sym) {
+ if (is_contained(Config.SymbolsToKeep, Sym.Name) ||
+ (Config.KeepFileSymbols && Sym.Type == STT_FILE))
+ return false;
+
+ if (Config.DiscardAll && Sym.Binding == STB_LOCAL &&
+ Sym.getShndx() != SHN_UNDEF && Sym.Type != STT_FILE &&
+ Sym.Type != STT_SECTION)
+ return true;
+
+ if (Config.StripAll || Config.StripAllGNU)
+ return true;
+
+ if (is_contained(Config.SymbolsToRemove, Sym.Name))
+ return true;
+
+ if (Config.StripUnneeded && !Sym.Referenced &&
+ (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) &&
+ Sym.Type != STT_FILE && Sym.Type != STT_SECTION)
+ return true;
+
+ return false;
+ });
+ }
+
+ SectionPred RemovePred = [](const SectionBase &) { return false; };
+
+ // Removes:
+ if (!Config.ToRemove.empty()) {
+ RemovePred = [&Config](const SectionBase &Sec) {
+ return is_contained(Config.ToRemove, Sec.Name);
+ };
+ }
+
+ if (Config.StripDWO || !Config.SplitDWO.empty())
+ RemovePred = [RemovePred](const SectionBase &Sec) {
+ return isDWOSection(Sec) || RemovePred(Sec);
+ };
+
+ if (Config.ExtractDWO)
+ RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
+ return onlyKeepDWOPred(Obj, Sec) || RemovePred(Sec);
+ };
+
+ if (Config.StripAllGNU)
+ RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
+ if (RemovePred(Sec))
+ return true;
+ if ((Sec.Flags & SHF_ALLOC) != 0)
+ return false;
+ if (&Sec == Obj.SectionNames)
+ return false;
+ switch (Sec.Type) {
+ case SHT_SYMTAB:
+ case SHT_REL:
+ case SHT_RELA:
+ case SHT_STRTAB:
+ return true;
+ }
+ return isDebugSection(Sec);
+ };
+
+ if (Config.StripSections) {
+ RemovePred = [RemovePred](const SectionBase &Sec) {
+ return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0;
+ };
+ }
+
+ if (Config.StripDebug) {
+ RemovePred = [RemovePred](const SectionBase &Sec) {
+ return RemovePred(Sec) || isDebugSection(Sec);
+ };
+ }
+
+ if (Config.StripNonAlloc)
+ RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
+ if (RemovePred(Sec))
+ return true;
+ if (&Sec == Obj.SectionNames)
+ return false;
+ return (Sec.Flags & SHF_ALLOC) == 0;
+ };
+
+ if (Config.StripAll)
+ RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
+ if (RemovePred(Sec))
+ return true;
+ if (&Sec == Obj.SectionNames)
+ return false;
+ if (StringRef(Sec.Name).startswith(".gnu.warning"))
+ return false;
+ return (Sec.Flags & SHF_ALLOC) == 0;
+ };
+
+ // Explicit copies:
+ if (!Config.OnlySection.empty()) {
+ RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) {
+ // Explicitly keep these sections regardless of previous removes.
+ if (is_contained(Config.OnlySection, Sec.Name))
+ return false;
+
+ // Allow all implicit removes.
+ if (RemovePred(Sec))
+ return true;
+
+ // Keep special sections.
+ if (Obj.SectionNames == &Sec)
+ return false;
+ if (Obj.SymbolTable == &Sec ||
+ (Obj.SymbolTable && Obj.SymbolTable->getStrTab() == &Sec))
+ return false;
+
+ // Remove everything else.
+ return true;
+ };
+ }
+
+ if (!Config.KeepSection.empty()) {
+ RemovePred = [&Config, RemovePred](const SectionBase &Sec) {
+ // Explicitly keep these sections regardless of previous removes.
+ if (is_contained(Config.KeepSection, Sec.Name))
+ return false;
+ // Otherwise defer to RemovePred.
+ return RemovePred(Sec);
+ };
+ }
+
+ // This has to be the last predicate assignment.
+ // If the option --keep-symbol has been specified
+ // and at least one of those symbols is present
+ // (equivalently, the updated symbol table is not empty)
+ // the symbol table and the string table should not be removed.
+ if ((!Config.SymbolsToKeep.empty() || Config.KeepFileSymbols) &&
+ Obj.SymbolTable && !Obj.SymbolTable->empty()) {
+ RemovePred = [&Obj, RemovePred](const SectionBase &Sec) {
+ if (&Sec == Obj.SymbolTable || &Sec == Obj.SymbolTable->getStrTab())
+ return false;
+ return RemovePred(Sec);
+ };
+ }
+
+ if (Config.CompressionType != DebugCompressionType::None)
+ replaceDebugSections(Config, Obj, RemovePred, isCompressable,
+ [&Config, &Obj](const SectionBase *S) {
+ return &Obj.addSection<CompressedSection>(
+ *S, Config.CompressionType);
+ });
+ else if (Config.DecompressDebugSections)
+ replaceDebugSections(
+ Config, Obj, RemovePred,
+ [](const SectionBase &S) { return isa<CompressedSection>(&S); },
+ [&Obj](const SectionBase *S) {
+ auto CS = cast<CompressedSection>(S);
+ return &Obj.addSection<DecompressedSection>(*CS);
+ });
+
+ Obj.removeSections(RemovePred);
+
+ if (!Config.SectionsToRename.empty()) {
+ for (auto &Sec : Obj.sections()) {
+ const auto Iter = Config.SectionsToRename.find(Sec.Name);
+ if (Iter != Config.SectionsToRename.end()) {
+ const SectionRename &SR = Iter->second;
+ Sec.Name = SR.NewName;
+ if (SR.NewFlags.hasValue()) {
+ // Preserve some flags which should not be dropped when setting flags.
+ // Also, preserve anything OS/processor dependant.
+ const uint64_t PreserveMask = ELF::SHF_COMPRESSED | ELF::SHF_EXCLUDE |
+ ELF::SHF_GROUP | ELF::SHF_LINK_ORDER |
+ ELF::SHF_MASKOS | ELF::SHF_MASKPROC |
+ ELF::SHF_TLS | ELF::SHF_INFO_LINK;
+ Sec.Flags = (Sec.Flags & PreserveMask) |
+ (SR.NewFlags.getValue() & ~PreserveMask);
+ }
+ }
+ }
+ }
+
+ if (!Config.AddSection.empty()) {
+ for (const auto &Flag : Config.AddSection) {
+ std::pair<StringRef, StringRef> SecPair = Flag.split("=");
+ StringRef SecName = SecPair.first;
+ StringRef File = SecPair.second;
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(File);
+ if (!BufOrErr)
+ reportError(File, BufOrErr.getError());
+ std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr);
+ ArrayRef<uint8_t> Data(
+ reinterpret_cast<const uint8_t *>(Buf->getBufferStart()),
+ Buf->getBufferSize());
+ OwnedDataSection &NewSection =
+ Obj.addSection<OwnedDataSection>(SecName, Data);
+ if (SecName.startswith(".note") && SecName != ".note.GNU-stack")
+ NewSection.Type = SHT_NOTE;
+ }
+ }
+
+ if (!Config.DumpSection.empty()) {
+ for (const auto &Flag : Config.DumpSection) {
+ std::pair<StringRef, StringRef> SecPair = Flag.split("=");
+ StringRef SecName = SecPair.first;
+ StringRef File = SecPair.second;
+ if (Error E = dumpSectionToFile(SecName, File, Obj))
+ reportError(Config.InputFilename, std::move(E));
+ }
+ }
+
+ if (!Config.AddGnuDebugLink.empty())
+ Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink);
+}
+
+void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
+ Buffer &Out) {
+ BinaryReader Reader(Config.BinaryArch, &In);
+ std::unique_ptr<Object> Obj = Reader.create();
+
+ // Prefer OutputArch (-O<format>) if set, otherwise fallback to BinaryArch
+ // (-B<arch>).
+ const ElfType OutputElfType = getOutputElfType(
+ Config.OutputArch ? Config.OutputArch.getValue() : Config.BinaryArch);
+ handleArgs(Config, *Obj, Reader, OutputElfType);
+ std::unique_ptr<Writer> Writer =
+ createWriter(Config, *Obj, Out, OutputElfType);
+ Writer->finalize();
+ Writer->write();
+}
+
+void executeObjcopyOnBinary(const CopyConfig &Config,
+ object::ELFObjectFileBase &In, Buffer &Out) {
+ ELFReader Reader(&In);
+ std::unique_ptr<Object> Obj = Reader.create();
+ // Prefer OutputArch (-O<format>) if set, otherwise infer it from the input.
+ const ElfType OutputElfType =
+ Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue())
+ : getOutputElfType(In);
+ ArrayRef<uint8_t> BuildIdBytes;
+
+ if (!Config.BuildIdLinkDir.empty()) {
+ BuildIdBytes = unwrapOrError(findBuildID(In));
+ if (BuildIdBytes.size() < 2)
+ error("build ID in file '" + Config.InputFilename +
+ "' is smaller than two bytes");
+ }
+
+ if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkInput) {
+ linkToBuildIdDir(Config, Config.InputFilename,
+ Config.BuildIdLinkInput.getValue(), BuildIdBytes);
+ }
+ handleArgs(Config, *Obj, Reader, OutputElfType);
+ std::unique_ptr<Writer> Writer =
+ createWriter(Config, *Obj, Out, OutputElfType);
+ Writer->finalize();
+ Writer->write();
+ if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput) {
+ linkToBuildIdDir(Config, Config.OutputFilename,
+ Config.BuildIdLinkOutput.getValue(), BuildIdBytes);
+ }
+}
+
+} // end namespace elf
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/tools/llvm-objcopy/ELF/ELFObjcopy.h b/tools/llvm-objcopy/ELF/ELFObjcopy.h
new file mode 100644
index 000000000000..43f41c00ce5b
--- /dev/null
+++ b/tools/llvm-objcopy/ELF/ELFObjcopy.h
@@ -0,0 +1,34 @@
+//===- ELFObjcopy.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H
+#define LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H
+
+namespace llvm {
+class MemoryBuffer;
+
+namespace object {
+class ELFObjectFileBase;
+} // end namespace object
+
+namespace objcopy {
+struct CopyConfig;
+class Buffer;
+
+namespace elf {
+void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
+ Buffer &Out);
+void executeObjcopyOnBinary(const CopyConfig &Config,
+ object::ELFObjectFileBase &In, Buffer &Out);
+
+} // end namespace elf
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H
diff --git a/tools/llvm-objcopy/Object.cpp b/tools/llvm-objcopy/ELF/Object.cpp
index 7e88f5263a39..3d3e029c09eb 100644
--- a/tools/llvm-objcopy/Object.cpp
+++ b/tools/llvm-objcopy/ELF/Object.cpp
@@ -15,7 +15,9 @@
#include "llvm/ADT/Twine.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/MC/MCTargetOptions.h"
#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Support/Compression.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/Path.h"
@@ -26,45 +28,14 @@
#include <utility>
#include <vector>
-using namespace llvm;
-using namespace llvm::objcopy;
+namespace llvm {
+namespace objcopy {
+namespace elf {
+
using namespace object;
using namespace ELF;
-Buffer::~Buffer() {}
-
-void FileBuffer::allocate(size_t Size) {
- Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
- FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable);
- handleAllErrors(BufferOrErr.takeError(), [this](const ErrorInfoBase &E) {
- error("failed to open " + getName() + ": " + E.message());
- });
- Buf = std::move(*BufferOrErr);
-}
-
-Error FileBuffer::commit() { return Buf->commit(); }
-
-uint8_t *FileBuffer::getBufferStart() {
- return reinterpret_cast<uint8_t *>(Buf->getBufferStart());
-}
-
-void MemBuffer::allocate(size_t Size) {
- Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName());
-}
-
-Error MemBuffer::commit() { return Error::success(); }
-
-uint8_t *MemBuffer::getBufferStart() {
- return reinterpret_cast<uint8_t *>(Buf->getBufferStart());
-}
-
-std::unique_ptr<WritableMemoryBuffer> MemBuffer::releaseMemoryBuffer() {
- return std::move(Buf);
-}
-
template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) {
- using Elf_Phdr = typename ELFT::Phdr;
-
uint8_t *B = Buf.getBufferStart();
B += Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr);
Elf_Phdr &Phdr = *reinterpret_cast<Elf_Phdr *>(B);
@@ -87,7 +58,7 @@ void SectionBase::markSymbols() {}
template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) {
uint8_t *B = Buf.getBufferStart();
B += Sec.HeaderOffset;
- typename ELFT::Shdr &Shdr = *reinterpret_cast<typename ELFT::Shdr *>(B);
+ Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(B);
Shdr.sh_name = Sec.NameIndex;
Shdr.sh_type = Sec.Type;
Shdr.sh_flags = Sec.Flags;
@@ -100,7 +71,46 @@ template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) {
Shdr.sh_entsize = Sec.EntrySize;
}
-SectionVisitor::~SectionVisitor() {}
+template <class ELFT> void ELFSectionSizer<ELFT>::visit(Section &Sec) {}
+
+template <class ELFT>
+void ELFSectionSizer<ELFT>::visit(OwnedDataSection &Sec) {}
+
+template <class ELFT>
+void ELFSectionSizer<ELFT>::visit(StringTableSection &Sec) {}
+
+template <class ELFT>
+void ELFSectionSizer<ELFT>::visit(DynamicRelocationSection &Sec) {}
+
+template <class ELFT>
+void ELFSectionSizer<ELFT>::visit(SymbolTableSection &Sec) {
+ Sec.EntrySize = sizeof(Elf_Sym);
+ Sec.Size = Sec.Symbols.size() * Sec.EntrySize;
+ // Align to the largest field in Elf_Sym.
+ Sec.Align = ELFT::Is64Bits ? sizeof(Elf_Xword) : sizeof(Elf_Word);
+}
+
+template <class ELFT>
+void ELFSectionSizer<ELFT>::visit(RelocationSection &Sec) {
+ Sec.EntrySize = Sec.Type == SHT_REL ? sizeof(Elf_Rel) : sizeof(Elf_Rela);
+ Sec.Size = Sec.Relocations.size() * Sec.EntrySize;
+ // Align to the largest field in Elf_Rel(a).
+ Sec.Align = ELFT::Is64Bits ? sizeof(Elf_Xword) : sizeof(Elf_Word);
+}
+
+template <class ELFT>
+void ELFSectionSizer<ELFT>::visit(GnuDebugLinkSection &Sec) {}
+
+template <class ELFT> void ELFSectionSizer<ELFT>::visit(GroupSection &Sec) {}
+
+template <class ELFT>
+void ELFSectionSizer<ELFT>::visit(SectionIndexSection &Sec) {}
+
+template <class ELFT>
+void ELFSectionSizer<ELFT>::visit(CompressedSection &Sec) {}
+
+template <class ELFT>
+void ELFSectionSizer<ELFT>::visit(DecompressedSection &Sec) {}
void BinarySectionWriter::visit(const SectionIndexSection &Sec) {
error("Cannot write symbol section index table '" + Sec.Name + "' ");
@@ -126,20 +136,169 @@ void SectionWriter::visit(const Section &Sec) {
if (Sec.Type == SHT_NOBITS)
return;
uint8_t *Buf = Out.getBufferStart() + Sec.Offset;
- std::copy(std::begin(Sec.Contents), std::end(Sec.Contents), Buf);
+ llvm::copy(Sec.Contents, Buf);
}
void Section::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); }
+void Section::accept(MutableSectionVisitor &Visitor) { Visitor.visit(*this); }
+
void SectionWriter::visit(const OwnedDataSection &Sec) {
uint8_t *Buf = Out.getBufferStart() + Sec.Offset;
- std::copy(std::begin(Sec.Data), std::end(Sec.Data), Buf);
+ llvm::copy(Sec.Data, Buf);
+}
+
+static const std::vector<uint8_t> ZlibGnuMagic = {'Z', 'L', 'I', 'B'};
+
+static bool isDataGnuCompressed(ArrayRef<uint8_t> Data) {
+ return Data.size() > ZlibGnuMagic.size() &&
+ std::equal(ZlibGnuMagic.begin(), ZlibGnuMagic.end(), Data.data());
+}
+
+template <class ELFT>
+static std::tuple<uint64_t, uint64_t>
+getDecompressedSizeAndAlignment(ArrayRef<uint8_t> Data) {
+ const bool IsGnuDebug = isDataGnuCompressed(Data);
+ const uint64_t DecompressedSize =
+ IsGnuDebug
+ ? support::endian::read64be(reinterpret_cast<const uint64_t *>(
+ Data.data() + ZlibGnuMagic.size()))
+ : reinterpret_cast<const Elf_Chdr_Impl<ELFT> *>(Data.data())->ch_size;
+ const uint64_t DecompressedAlign =
+ IsGnuDebug ? 1
+ : reinterpret_cast<const Elf_Chdr_Impl<ELFT> *>(Data.data())
+ ->ch_addralign;
+
+ return std::make_tuple(DecompressedSize, DecompressedAlign);
+}
+
+template <class ELFT>
+void ELFSectionWriter<ELFT>::visit(const DecompressedSection &Sec) {
+ uint8_t *Buf = Out.getBufferStart() + Sec.Offset;
+
+ if (!zlib::isAvailable()) {
+ std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf);
+ return;
+ }
+
+ const size_t DataOffset = isDataGnuCompressed(Sec.OriginalData)
+ ? (ZlibGnuMagic.size() + sizeof(Sec.Size))
+ : sizeof(Elf_Chdr_Impl<ELFT>);
+
+ StringRef CompressedContent(
+ reinterpret_cast<const char *>(Sec.OriginalData.data()) + DataOffset,
+ Sec.OriginalData.size() - DataOffset);
+
+ SmallVector<char, 128> DecompressedContent;
+ if (Error E = zlib::uncompress(CompressedContent, DecompressedContent,
+ static_cast<size_t>(Sec.Size)))
+ reportError(Sec.Name, std::move(E));
+
+ std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf);
+}
+
+void BinarySectionWriter::visit(const DecompressedSection &Sec) {
+ error("Cannot write compressed section '" + Sec.Name + "' ");
+}
+
+void DecompressedSection::accept(SectionVisitor &Visitor) const {
+ Visitor.visit(*this);
+}
+
+void DecompressedSection::accept(MutableSectionVisitor &Visitor) {
+ Visitor.visit(*this);
}
void OwnedDataSection::accept(SectionVisitor &Visitor) const {
Visitor.visit(*this);
}
+void OwnedDataSection::accept(MutableSectionVisitor &Visitor) {
+ Visitor.visit(*this);
+}
+
+void BinarySectionWriter::visit(const CompressedSection &Sec) {
+ error("Cannot write compressed section '" + Sec.Name + "' ");
+}
+
+template <class ELFT>
+void ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) {
+ uint8_t *Buf = Out.getBufferStart();
+ Buf += Sec.Offset;
+
+ if (Sec.CompressionType == DebugCompressionType::None) {
+ std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf);
+ return;
+ }
+
+ if (Sec.CompressionType == DebugCompressionType::GNU) {
+ const char *Magic = "ZLIB";
+ memcpy(Buf, Magic, strlen(Magic));
+ Buf += strlen(Magic);
+ const uint64_t DecompressedSize =
+ support::endian::read64be(&Sec.DecompressedSize);
+ memcpy(Buf, &DecompressedSize, sizeof(DecompressedSize));
+ Buf += sizeof(DecompressedSize);
+ } else {
+ Elf_Chdr_Impl<ELFT> Chdr;
+ Chdr.ch_type = ELF::ELFCOMPRESS_ZLIB;
+ Chdr.ch_size = Sec.DecompressedSize;
+ Chdr.ch_addralign = Sec.DecompressedAlign;
+ memcpy(Buf, &Chdr, sizeof(Chdr));
+ Buf += sizeof(Chdr);
+ }
+
+ std::copy(Sec.CompressedData.begin(), Sec.CompressedData.end(), Buf);
+}
+
+CompressedSection::CompressedSection(const SectionBase &Sec,
+ DebugCompressionType CompressionType)
+ : SectionBase(Sec), CompressionType(CompressionType),
+ DecompressedSize(Sec.OriginalData.size()), DecompressedAlign(Sec.Align) {
+
+ if (!zlib::isAvailable()) {
+ CompressionType = DebugCompressionType::None;
+ return;
+ }
+
+ if (Error E = zlib::compress(
+ StringRef(reinterpret_cast<const char *>(OriginalData.data()),
+ OriginalData.size()),
+ CompressedData))
+ reportError(Name, std::move(E));
+
+ size_t ChdrSize;
+ if (CompressionType == DebugCompressionType::GNU) {
+ Name = ".z" + Sec.Name.substr(1);
+ ChdrSize = sizeof("ZLIB") - 1 + sizeof(uint64_t);
+ } else {
+ Flags |= ELF::SHF_COMPRESSED;
+ ChdrSize =
+ std::max(std::max(sizeof(object::Elf_Chdr_Impl<object::ELF64LE>),
+ sizeof(object::Elf_Chdr_Impl<object::ELF64BE>)),
+ std::max(sizeof(object::Elf_Chdr_Impl<object::ELF32LE>),
+ sizeof(object::Elf_Chdr_Impl<object::ELF32BE>)));
+ }
+ Size = ChdrSize + CompressedData.size();
+ Align = 8;
+}
+
+CompressedSection::CompressedSection(ArrayRef<uint8_t> CompressedData,
+ uint64_t DecompressedSize,
+ uint64_t DecompressedAlign)
+ : CompressionType(DebugCompressionType::None),
+ DecompressedSize(DecompressedSize), DecompressedAlign(DecompressedAlign) {
+ OriginalData = CompressedData;
+}
+
+void CompressedSection::accept(SectionVisitor &Visitor) const {
+ Visitor.visit(*this);
+}
+
+void CompressedSection::accept(MutableSectionVisitor &Visitor) {
+ Visitor.visit(*this);
+}
+
void StringTableSection::addString(StringRef Name) {
StrTabBuilder.add(Name);
Size = StrTabBuilder.getSize();
@@ -159,11 +318,15 @@ void StringTableSection::accept(SectionVisitor &Visitor) const {
Visitor.visit(*this);
}
+void StringTableSection::accept(MutableSectionVisitor &Visitor) {
+ Visitor.visit(*this);
+}
+
template <class ELFT>
void ELFSectionWriter<ELFT>::visit(const SectionIndexSection &Sec) {
uint8_t *Buf = Out.getBufferStart() + Sec.Offset;
- auto *IndexesBuffer = reinterpret_cast<typename ELFT::Word *>(Buf);
- std::copy(std::begin(Sec.Indexes), std::end(Sec.Indexes), IndexesBuffer);
+ auto *IndexesBuffer = reinterpret_cast<Elf_Word *>(Buf);
+ llvm::copy(Sec.Indexes, IndexesBuffer);
}
void SectionIndexSection::initialize(SectionTableRef SecTable) {
@@ -182,6 +345,10 @@ void SectionIndexSection::accept(SectionVisitor &Visitor) const {
Visitor.visit(*this);
}
+void SectionIndexSection::accept(MutableSectionVisitor &Visitor) {
+ Visitor.visit(*this);
+}
+
static bool isValidReservedSectionIndex(uint16_t Index, uint16_t Machine) {
switch (Index) {
case SHN_ABS:
@@ -226,18 +393,20 @@ uint16_t Symbol::getShndx() const {
llvm_unreachable("Symbol with invalid ShndxType encountered");
}
+bool Symbol::isCommon() const { return getShndx() == SHN_COMMON; }
+
void SymbolTableSection::assignIndices() {
uint32_t Index = 0;
for (auto &Sym : Symbols)
Sym->Index = Index++;
}
-void SymbolTableSection::addSymbol(StringRef Name, uint8_t Bind, uint8_t Type,
+void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type,
SectionBase *DefinedIn, uint64_t Value,
uint8_t Visibility, uint16_t Shndx,
- uint64_t Sz) {
+ uint64_t Size) {
Symbol Sym;
- Sym.Name = Name;
+ Sym.Name = Name.str();
Sym.Binding = Bind;
Sym.Type = Type;
Sym.DefinedIn = DefinedIn;
@@ -251,7 +420,7 @@ void SymbolTableSection::addSymbol(StringRef Name, uint8_t Bind, uint8_t Type,
}
Sym.Value = Value;
Sym.Visibility = Visibility;
- Sym.Size = Sz;
+ Sym.Size = Size;
Sym.Index = Symbols.size();
Symbols.emplace_back(llvm::make_unique<Symbol>(Sym));
Size += this->EntrySize;
@@ -344,7 +513,7 @@ template <class ELFT>
void ELFSectionWriter<ELFT>::visit(const SymbolTableSection &Sec) {
uint8_t *Buf = Out.getBufferStart();
Buf += Sec.Offset;
- typename ELFT::Sym *Sym = reinterpret_cast<typename ELFT::Sym *>(Buf);
+ Elf_Sym *Sym = reinterpret_cast<Elf_Sym *>(Buf);
// Loop though symbols setting each entry of the symbol table.
for (auto &Symbol : Sec.Symbols) {
Sym->st_name = Symbol->NameIndex;
@@ -362,6 +531,10 @@ void SymbolTableSection::accept(SectionVisitor &Visitor) const {
Visitor.visit(*this);
}
+void SymbolTableSection::accept(MutableSectionVisitor &Visitor) {
+ Visitor.visit(*this);
+}
+
template <class SymTabType>
void RelocSectionWithSymtabBase<SymTabType>::removeSectionReferences(
const SectionBase *Sec) {
@@ -377,11 +550,13 @@ void RelocSectionWithSymtabBase<SymTabType>::removeSectionReferences(
template <class SymTabType>
void RelocSectionWithSymtabBase<SymTabType>::initialize(
SectionTableRef SecTable) {
- setSymTab(SecTable.getSectionOfType<SymTabType>(
- Link,
- "Link field value " + Twine(Link) + " in section " + Name + " is invalid",
- "Link field value " + Twine(Link) + " in section " + Name +
- " is not a symbol table"));
+ if (Link != SHN_UNDEF)
+ setSymTab(SecTable.getSectionOfType<SymTabType>(
+ Link,
+ "Link field value " + Twine(Link) + " in section " + Name +
+ " is invalid",
+ "Link field value " + Twine(Link) + " in section " + Name +
+ " is not a symbol table"));
if (Info != SHN_UNDEF)
setSection(SecTable.getSection(Info, "Info field value " + Twine(Info) +
@@ -393,7 +568,8 @@ void RelocSectionWithSymtabBase<SymTabType>::initialize(
template <class SymTabType>
void RelocSectionWithSymtabBase<SymTabType>::finalize() {
- this->Link = Symbols->Index;
+ this->Link = Symbols ? Symbols->Index : 0;
+
if (SecToApplyRel != nullptr)
this->Info = SecToApplyRel->Index;
}
@@ -429,11 +605,15 @@ void RelocationSection::accept(SectionVisitor &Visitor) const {
Visitor.visit(*this);
}
+void RelocationSection::accept(MutableSectionVisitor &Visitor) {
+ Visitor.visit(*this);
+}
+
void RelocationSection::removeSymbols(
function_ref<bool(const Symbol &)> ToRemove) {
for (const Relocation &Reloc : Relocations)
if (ToRemove(*Reloc.RelocSymbol))
- error("not stripping symbol `" + Reloc.RelocSymbol->Name +
+ error("not stripping symbol '" + Reloc.RelocSymbol->Name +
"' because it is named in a relocation");
}
@@ -443,7 +623,7 @@ void RelocationSection::markSymbols() {
}
void SectionWriter::visit(const DynamicRelocationSection &Sec) {
- std::copy(std::begin(Sec.Contents), std::end(Sec.Contents),
+ llvm::copy(Sec.Contents,
Out.getBufferStart() + Sec.Offset);
}
@@ -451,6 +631,10 @@ void DynamicRelocationSection::accept(SectionVisitor &Visitor) const {
Visitor.visit(*this);
}
+void DynamicRelocationSection::accept(MutableSectionVisitor &Visitor) {
+ Visitor.visit(*this);
+}
+
void Section::removeSectionReferences(const SectionBase *Sec) {
if (LinkSection == Sec) {
error("Section " + LinkSection->Name +
@@ -506,12 +690,12 @@ void GnuDebugLinkSection::init(StringRef File, StringRef Data) {
// establish the order that sections should go in. By using the maximum
// possible offset we cause this section to wind up at the end.
OriginalOffset = std::numeric_limits<uint64_t>::max();
- JamCRC crc;
- crc.update(ArrayRef<char>(Data.data(), Data.size()));
+ JamCRC CRC;
+ CRC.update(ArrayRef<char>(Data.data(), Data.size()));
// The CRC32 value needs to be complemented because the JamCRC dosn't
// finalize the CRC32 value. It also dosn't negate the initial CRC32 value
// but it starts by default at 0xFFFFFFFF which is the complement of zero.
- CRC32 = ~crc.getCRC();
+ CRC32 = ~CRC.getCRC();
}
GnuDebugLinkSection::GnuDebugLinkSection(StringRef File) : FileName(File) {
@@ -530,13 +714,17 @@ void ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) {
Elf_Word *CRC =
reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word));
*CRC = Sec.CRC32;
- std::copy(std::begin(Sec.FileName), std::end(Sec.FileName), File);
+ llvm::copy(Sec.FileName, File);
}
void GnuDebugLinkSection::accept(SectionVisitor &Visitor) const {
Visitor.visit(*this);
}
+void GnuDebugLinkSection::accept(MutableSectionVisitor &Visitor) {
+ Visitor.visit(*this);
+}
+
template <class ELFT>
void ELFSectionWriter<ELFT>::visit(const GroupSection &Sec) {
ELF::Elf32_Word *Buf =
@@ -550,6 +738,10 @@ void GroupSection::accept(SectionVisitor &Visitor) const {
Visitor.visit(*this);
}
+void GroupSection::accept(MutableSectionVisitor &Visitor) {
+ Visitor.visit(*this);
+}
+
// Returns true IFF a section is wholly inside the range of a segment
static bool sectionWithinSegment(const SectionBase &Section,
const Segment &Segment) {
@@ -589,6 +781,79 @@ static bool compareSegmentsByPAddr(const Segment *A, const Segment *B) {
return A->Index < B->Index;
}
+void BinaryELFBuilder::initFileHeader() {
+ Obj->Flags = 0x0;
+ Obj->Type = ET_REL;
+ Obj->OSABI = ELFOSABI_NONE;
+ Obj->ABIVersion = 0;
+ Obj->Entry = 0x0;
+ Obj->Machine = EMachine;
+ Obj->Version = 1;
+}
+
+void BinaryELFBuilder::initHeaderSegment() { Obj->ElfHdrSegment.Index = 0; }
+
+StringTableSection *BinaryELFBuilder::addStrTab() {
+ auto &StrTab = Obj->addSection<StringTableSection>();
+ StrTab.Name = ".strtab";
+
+ Obj->SectionNames = &StrTab;
+ return &StrTab;
+}
+
+SymbolTableSection *BinaryELFBuilder::addSymTab(StringTableSection *StrTab) {
+ auto &SymTab = Obj->addSection<SymbolTableSection>();
+
+ SymTab.Name = ".symtab";
+ SymTab.Link = StrTab->Index;
+
+ // The symbol table always needs a null symbol
+ SymTab.addSymbol("", 0, 0, nullptr, 0, 0, 0, 0);
+
+ Obj->SymbolTable = &SymTab;
+ return &SymTab;
+}
+
+void BinaryELFBuilder::addData(SymbolTableSection *SymTab) {
+ auto Data = ArrayRef<uint8_t>(
+ reinterpret_cast<const uint8_t *>(MemBuf->getBufferStart()),
+ MemBuf->getBufferSize());
+ auto &DataSection = Obj->addSection<Section>(Data);
+ DataSection.Name = ".data";
+ DataSection.Type = ELF::SHT_PROGBITS;
+ DataSection.Size = Data.size();
+ DataSection.Flags = ELF::SHF_ALLOC | ELF::SHF_WRITE;
+
+ std::string SanitizedFilename = MemBuf->getBufferIdentifier().str();
+ std::replace_if(std::begin(SanitizedFilename), std::end(SanitizedFilename),
+ [](char C) { return !isalnum(C); }, '_');
+ Twine Prefix = Twine("_binary_") + SanitizedFilename;
+
+ SymTab->addSymbol(Prefix + "_start", STB_GLOBAL, STT_NOTYPE, &DataSection,
+ /*Value=*/0, STV_DEFAULT, 0, 0);
+ SymTab->addSymbol(Prefix + "_end", STB_GLOBAL, STT_NOTYPE, &DataSection,
+ /*Value=*/DataSection.Size, STV_DEFAULT, 0, 0);
+ SymTab->addSymbol(Prefix + "_size", STB_GLOBAL, STT_NOTYPE, nullptr,
+ /*Value=*/DataSection.Size, STV_DEFAULT, SHN_ABS, 0);
+}
+
+void BinaryELFBuilder::initSections() {
+ for (auto &Section : Obj->sections()) {
+ Section.initialize(Obj->sections());
+ }
+}
+
+std::unique_ptr<Object> BinaryELFBuilder::build() {
+ initFileHeader();
+ initHeaderSegment();
+ StringTableSection *StrTab = addStrTab();
+ SymbolTableSection *SymTab = addSymTab(StrTab);
+ initSections();
+ addData(SymTab);
+
+ return std::move(Obj);
+}
+
template <class ELFT> void ELFBuilder<ELFT>::setParentSegment(Segment &Child) {
for (auto &Parent : Obj.segments()) {
// Every segment will overlap with itself but we don't want a segment to
@@ -633,15 +898,6 @@ template <class ELFT> void ELFBuilder<ELFT>::readProgramHeaders() {
}
auto &ElfHdr = Obj.ElfHdrSegment;
- // Creating multiple PT_PHDR segments technically is not valid, but PT_LOAD
- // segments must not overlap, and other types fit even less.
- ElfHdr.Type = PT_PHDR;
- ElfHdr.Flags = 0;
- ElfHdr.OriginalOffset = ElfHdr.Offset = 0;
- ElfHdr.VAddr = 0;
- ElfHdr.PAddr = 0;
- ElfHdr.FileSize = ElfHdr.MemSize = sizeof(Elf_Ehdr);
- ElfHdr.Align = 0;
ElfHdr.Index = Index++;
const auto &Ehdr = *ElfFile.getHeader();
@@ -725,8 +981,7 @@ void ELFBuilder<ELFT>::initSymbolTable(SymbolTableSection *SymTab) {
Elf_Word Index = ShndxData[&Sym - Symbols.begin()];
DefSection = Obj.sections().getSection(
Index,
- "Symbol '" + Name + "' has invalid section index " +
- Twine(Index));
+ "Symbol '" + Name + "' has invalid section index " + Twine(Index));
} else if (Sym.st_shndx >= SHN_LORESERVE) {
if (!isValidReservedSectionIndex(Sym.st_shndx, Obj.Machine)) {
error(
@@ -828,10 +1083,20 @@ SectionBase &ELFBuilder<ELFT>::makeSection(const Elf_Shdr &Shdr) {
}
case SHT_NOBITS:
return Obj.addSection<Section>(Data);
- default:
+ default: {
Data = unwrapOrError(ElfFile.getSectionContents(&Shdr));
+
+ if (isDataGnuCompressed(Data) || (Shdr.sh_flags & ELF::SHF_COMPRESSED)) {
+ uint64_t DecompressedSize, DecompressedAlign;
+ std::tie(DecompressedSize, DecompressedAlign) =
+ getDecompressedSizeAndAlignment<ELFT>(Data);
+ return Obj.addSection<CompressedSection>(Data, DecompressedSize,
+ DecompressedAlign);
+ }
+
return Obj.addSection<Section>(Data);
}
+ }
}
template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() {
@@ -854,6 +1119,9 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() {
Sec.Align = Shdr.sh_addralign;
Sec.EntrySize = Shdr.sh_entsize;
Sec.Index = Index++;
+ Sec.OriginalData =
+ ArrayRef<uint8_t>(ElfFile.base() + Shdr.sh_offset,
+ (Shdr.sh_type == SHT_NOBITS) ? 0 : Shdr.sh_size);
}
// If a section index table exists we'll need to initialize it before we
@@ -894,7 +1162,8 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() {
template <class ELFT> void ELFBuilder<ELFT>::build() {
const auto &Ehdr = *ElfFile.getHeader();
- std::copy(Ehdr.e_ident, Ehdr.e_ident + 16, Obj.Ident);
+ Obj.OSABI = Ehdr.e_ident[EI_OSABI];
+ Obj.ABIVersion = Ehdr.e_ident[EI_ABIVERSION];
Obj.Type = Ehdr.e_type;
Obj.Machine = Ehdr.e_machine;
Obj.Version = Ehdr.e_version;
@@ -926,34 +1195,26 @@ Writer::~Writer() {}
Reader::~Reader() {}
-ElfType ELFReader::getElfType() const {
- if (isa<ELFObjectFile<ELF32LE>>(Bin))
- return ELFT_ELF32LE;
- if (isa<ELFObjectFile<ELF64LE>>(Bin))
- return ELFT_ELF64LE;
- if (isa<ELFObjectFile<ELF32BE>>(Bin))
- return ELFT_ELF32BE;
- if (isa<ELFObjectFile<ELF64BE>>(Bin))
- return ELFT_ELF64BE;
- llvm_unreachable("Invalid ELFType");
+std::unique_ptr<Object> BinaryReader::create() const {
+ return BinaryELFBuilder(MInfo.EMachine, MemBuf).build();
}
std::unique_ptr<Object> ELFReader::create() const {
auto Obj = llvm::make_unique<Object>();
- if (auto *o = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) {
- ELFBuilder<ELF32LE> Builder(*o, *Obj);
+ if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) {
+ ELFBuilder<ELF32LE> Builder(*O, *Obj);
Builder.build();
return Obj;
- } else if (auto *o = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) {
- ELFBuilder<ELF64LE> Builder(*o, *Obj);
+ } else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) {
+ ELFBuilder<ELF64LE> Builder(*O, *Obj);
Builder.build();
return Obj;
- } else if (auto *o = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) {
- ELFBuilder<ELF32BE> Builder(*o, *Obj);
+ } else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) {
+ ELFBuilder<ELF32BE> Builder(*O, *Obj);
Builder.build();
return Obj;
- } else if (auto *o = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) {
- ELFBuilder<ELF64BE> Builder(*o, *Obj);
+ } else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) {
+ ELFBuilder<ELF64BE> Builder(*O, *Obj);
Builder.build();
return Obj;
}
@@ -963,18 +1224,31 @@ std::unique_ptr<Object> ELFReader::create() const {
template <class ELFT> void ELFWriter<ELFT>::writeEhdr() {
uint8_t *B = Buf.getBufferStart();
Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(B);
- std::copy(Obj.Ident, Obj.Ident + 16, Ehdr.e_ident);
+ std::fill(Ehdr.e_ident, Ehdr.e_ident + 16, 0);
+ Ehdr.e_ident[EI_MAG0] = 0x7f;
+ Ehdr.e_ident[EI_MAG1] = 'E';
+ Ehdr.e_ident[EI_MAG2] = 'L';
+ Ehdr.e_ident[EI_MAG3] = 'F';
+ Ehdr.e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32;
+ Ehdr.e_ident[EI_DATA] =
+ ELFT::TargetEndianness == support::big ? ELFDATA2MSB : ELFDATA2LSB;
+ Ehdr.e_ident[EI_VERSION] = EV_CURRENT;
+ Ehdr.e_ident[EI_OSABI] = Obj.OSABI;
+ Ehdr.e_ident[EI_ABIVERSION] = Obj.ABIVersion;
+
Ehdr.e_type = Obj.Type;
Ehdr.e_machine = Obj.Machine;
Ehdr.e_version = Obj.Version;
Ehdr.e_entry = Obj.Entry;
- Ehdr.e_phoff = Obj.ProgramHdrSegment.Offset;
+ // We have to use the fully-qualified name llvm::size
+ // since some compilers complain on ambiguous resolution.
+ Ehdr.e_phnum = llvm::size(Obj.segments());
+ Ehdr.e_phoff = (Ehdr.e_phnum != 0) ? Obj.ProgramHdrSegment.Offset : 0;
+ Ehdr.e_phentsize = (Ehdr.e_phnum != 0) ? sizeof(Elf_Phdr) : 0;
Ehdr.e_flags = Obj.Flags;
Ehdr.e_ehsize = sizeof(Elf_Ehdr);
- Ehdr.e_phentsize = sizeof(Elf_Phdr);
- Ehdr.e_phnum = size(Obj.segments());
- Ehdr.e_shentsize = sizeof(Elf_Shdr);
- if (WriteSectionHeaders) {
+ if (WriteSectionHeaders && size(Obj.sections()) != 0) {
+ Ehdr.e_shentsize = sizeof(Elf_Shdr);
Ehdr.e_shoff = Obj.SHOffset;
// """
// If the number of sections is greater than or equal to
@@ -998,6 +1272,7 @@ template <class ELFT> void ELFWriter<ELFT>::writeEhdr() {
else
Ehdr.e_shstrndx = Obj.SectionNames->Index;
} else {
+ Ehdr.e_shentsize = 0;
Ehdr.e_shoff = 0;
Ehdr.e_shnum = 0;
Ehdr.e_shstrndx = 0;
@@ -1106,7 +1381,7 @@ static uint64_t alignToAddr(uint64_t Offset, uint64_t Addr, uint64_t Align) {
}
// Orders segments such that if x = y->ParentSegment then y comes before x.
-static void OrderSegments(std::vector<Segment *> &Segments) {
+static void orderSegments(std::vector<Segment *> &Segments) {
std::stable_sort(std::begin(Segments), std::end(Segments),
compareSegmentsByOffset);
}
@@ -1148,7 +1423,7 @@ static uint64_t LayoutSegments(std::vector<Segment *> &Segments,
// sections had a ParentSegment or an offset one past the last section if there
// was a section that didn't have a ParentSegment.
template <class Range>
-static uint64_t LayoutSections(Range Sections, uint64_t Offset) {
+static uint64_t layoutSections(Range Sections, uint64_t Offset) {
// Now the offset of every segment has been set we can assign the offsets
// of each section. For sections that are covered by a segment we should use
// the segment's original offset and the section's original offset to compute
@@ -1172,6 +1447,17 @@ static uint64_t LayoutSections(Range Sections, uint64_t Offset) {
return Offset;
}
+template <class ELFT> void ELFWriter<ELFT>::initEhdrSegment() {
+ auto &ElfHdr = Obj.ElfHdrSegment;
+ ElfHdr.Type = PT_PHDR;
+ ElfHdr.Flags = 0;
+ ElfHdr.OriginalOffset = ElfHdr.Offset = 0;
+ ElfHdr.VAddr = 0;
+ ElfHdr.PAddr = 0;
+ ElfHdr.FileSize = ElfHdr.MemSize = sizeof(Elf_Ehdr);
+ ElfHdr.Align = 0;
+}
+
template <class ELFT> void ELFWriter<ELFT>::assignOffsets() {
// We need a temporary list of segments that has a special order to it
// so that we know that anytime ->ParentSegment is set that segment has
@@ -1181,17 +1467,17 @@ template <class ELFT> void ELFWriter<ELFT>::assignOffsets() {
OrderedSegments.push_back(&Segment);
OrderedSegments.push_back(&Obj.ElfHdrSegment);
OrderedSegments.push_back(&Obj.ProgramHdrSegment);
- OrderSegments(OrderedSegments);
+ orderSegments(OrderedSegments);
// Offset is used as the start offset of the first segment to be laid out.
// Since the ELF Header (ElfHdrSegment) must be at the start of the file,
// we start at offset 0.
uint64_t Offset = 0;
Offset = LayoutSegments(OrderedSegments, Offset);
- Offset = LayoutSections(Obj.sections(), Offset);
+ Offset = layoutSections(Obj.sections(), Offset);
// If we need to write the section header table out then we need to align the
// Offset so that SHOffset is valid.
if (WriteSectionHeaders)
- Offset = alignTo(Offset, sizeof(typename ELFT::Addr));
+ Offset = alignTo(Offset, sizeof(Elf_Addr));
Obj.SHOffset = Offset;
}
@@ -1263,10 +1549,17 @@ template <class ELFT> void ELFWriter<ELFT>::finalize() {
Obj.SectionNames->addString(Section.Name);
}
+ initEhdrSegment();
+
// Before we can prepare for layout the indexes need to be finalized.
+ // Also, the output arch may not be the same as the input arch, so fix up
+ // size-related fields before doing layout calculations.
uint64_t Index = 0;
- for (auto &Sec : Obj.sections())
+ auto SecSizer = llvm::make_unique<ELFSectionSizer<ELFT>>();
+ for (auto &Sec : Obj.sections()) {
Sec.Index = Index++;
+ Sec.accept(*SecSizer);
+ }
// The symbol table does not update all other sections on update. For
// instance, symbol names are not added as new symbols are added. This means
@@ -1324,10 +1617,10 @@ void BinaryWriter::finalize() {
// loading and physical addresses are intended for ROM loading.
// However, if no segment has a physical address, we'll fallback to using
// virtual addresses for all.
- if (std::all_of(std::begin(OrderedSegments), std::end(OrderedSegments),
- [](const Segment *Segment) { return Segment->PAddr == 0; }))
- for (const auto &Segment : OrderedSegments)
- Segment->PAddr = Segment->VAddr;
+ if (all_of(OrderedSegments,
+ [](const Segment *Seg) { return Seg->PAddr == 0; }))
+ for (Segment *Seg : OrderedSegments)
+ Seg->PAddr = Seg->VAddr;
std::stable_sort(std::begin(OrderedSegments), std::end(OrderedSegments),
compareSegmentsByPAddr);
@@ -1342,8 +1635,8 @@ void BinaryWriter::finalize() {
uint64_t Offset = 0;
// Modify the first segment so that there is no gap at the start. This allows
- // our layout algorithm to proceed as expected while not out writing out the
- // gap at the start.
+ // our layout algorithm to proceed as expected while not writing out the gap
+ // at the start.
if (!OrderedSegments.empty()) {
auto Seg = OrderedSegments[0];
auto Sec = Seg->firstSection();
@@ -1371,7 +1664,7 @@ void BinaryWriter::finalize() {
continue;
AllocatedSections.push_back(&Section);
}
- LayoutSections(make_pointee_range(AllocatedSections), Offset);
+ layoutSections(make_pointee_range(AllocatedSections), Offset);
// Now that every section has been laid out we just need to compute the total
// file size. This might not be the same as the offset returned by
@@ -1387,9 +1680,6 @@ void BinaryWriter::finalize() {
SecWriter = llvm::make_unique<BinarySectionWriter>(Buf);
}
-namespace llvm {
-namespace objcopy {
-
template class ELFBuilder<ELF64LE>;
template class ELFBuilder<ELF64BE>;
template class ELFBuilder<ELF32LE>;
@@ -1399,5 +1689,7 @@ template class ELFWriter<ELF64LE>;
template class ELFWriter<ELF64BE>;
template class ELFWriter<ELF32LE>;
template class ELFWriter<ELF32BE>;
+
+} // end namespace elf
} // end namespace objcopy
} // end namespace llvm
diff --git a/tools/llvm-objcopy/Object.h b/tools/llvm-objcopy/ELF/Object.h
index 76748d5fc641..e5730cd543ee 100644
--- a/tools/llvm-objcopy/Object.h
+++ b/tools/llvm-objcopy/ELF/Object.h
@@ -10,6 +10,8 @@
#ifndef LLVM_TOOLS_OBJCOPY_OBJECT_H
#define LLVM_TOOLS_OBJCOPY_OBJECT_H
+#include "Buffer.h"
+#include "CopyConfig.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
@@ -26,9 +28,10 @@
#include <vector>
namespace llvm {
+enum class DebugCompressionType;
namespace objcopy {
+namespace elf {
-class Buffer;
class SectionBase;
class Section;
class OwnedDataSection;
@@ -39,6 +42,8 @@ class DynamicRelocationSection;
class GnuDebugLinkSection;
class GroupSection;
class SectionIndexSection;
+class CompressedSection;
+class DecompressedSection;
class Segment;
class Object;
struct Symbol;
@@ -66,7 +71,7 @@ enum ElfType { ELFT_ELF32LE, ELFT_ELF64LE, ELFT_ELF32BE, ELFT_ELF64BE };
class SectionVisitor {
public:
- virtual ~SectionVisitor();
+ virtual ~SectionVisitor() = default;
virtual void visit(const Section &Sec) = 0;
virtual void visit(const OwnedDataSection &Sec) = 0;
@@ -77,6 +82,25 @@ public:
virtual void visit(const GnuDebugLinkSection &Sec) = 0;
virtual void visit(const GroupSection &Sec) = 0;
virtual void visit(const SectionIndexSection &Sec) = 0;
+ virtual void visit(const CompressedSection &Sec) = 0;
+ virtual void visit(const DecompressedSection &Sec) = 0;
+};
+
+class MutableSectionVisitor {
+public:
+ virtual ~MutableSectionVisitor() = default;
+
+ virtual void visit(Section &Sec) = 0;
+ virtual void visit(OwnedDataSection &Sec) = 0;
+ virtual void visit(StringTableSection &Sec) = 0;
+ virtual void visit(SymbolTableSection &Sec) = 0;
+ virtual void visit(RelocationSection &Sec) = 0;
+ virtual void visit(DynamicRelocationSection &Sec) = 0;
+ virtual void visit(GnuDebugLinkSection &Sec) = 0;
+ virtual void visit(GroupSection &Sec) = 0;
+ virtual void visit(SectionIndexSection &Sec) = 0;
+ virtual void visit(CompressedSection &Sec) = 0;
+ virtual void visit(DecompressedSection &Sec) = 0;
};
class SectionWriter : public SectionVisitor {
@@ -95,6 +119,8 @@ public:
virtual void visit(const GnuDebugLinkSection &Sec) override = 0;
virtual void visit(const GroupSection &Sec) override = 0;
virtual void visit(const SectionIndexSection &Sec) override = 0;
+ virtual void visit(const CompressedSection &Sec) override = 0;
+ virtual void visit(const DecompressedSection &Sec) override = 0;
explicit SectionWriter(Buffer &Buf) : Out(Buf) {}
};
@@ -104,6 +130,7 @@ private:
using Elf_Word = typename ELFT::Word;
using Elf_Rel = typename ELFT::Rel;
using Elf_Rela = typename ELFT::Rela;
+ using Elf_Sym = typename ELFT::Sym;
public:
virtual ~ELFSectionWriter() {}
@@ -112,13 +139,38 @@ public:
void visit(const GnuDebugLinkSection &Sec) override;
void visit(const GroupSection &Sec) override;
void visit(const SectionIndexSection &Sec) override;
+ void visit(const CompressedSection &Sec) override;
+ void visit(const DecompressedSection &Sec) override;
explicit ELFSectionWriter(Buffer &Buf) : SectionWriter(Buf) {}
};
+template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor {
+private:
+ using Elf_Rel = typename ELFT::Rel;
+ using Elf_Rela = typename ELFT::Rela;
+ using Elf_Sym = typename ELFT::Sym;
+ using Elf_Word = typename ELFT::Word;
+ using Elf_Xword = typename ELFT::Xword;
+
+public:
+ void visit(Section &Sec) override;
+ void visit(OwnedDataSection &Sec) override;
+ void visit(StringTableSection &Sec) override;
+ void visit(DynamicRelocationSection &Sec) override;
+ void visit(SymbolTableSection &Sec) override;
+ void visit(RelocationSection &Sec) override;
+ void visit(GnuDebugLinkSection &Sec) override;
+ void visit(GroupSection &Sec) override;
+ void visit(SectionIndexSection &Sec) override;
+ void visit(CompressedSection &Sec) override;
+ void visit(DecompressedSection &Sec) override;
+};
+
#define MAKE_SEC_WRITER_FRIEND \
friend class SectionWriter; \
- template <class ELFT> friend class ELFSectionWriter;
+ template <class ELFT> friend class ELFSectionWriter; \
+ template <class ELFT> friend class ELFSectionSizer;
class BinarySectionWriter : public SectionWriter {
public:
@@ -129,52 +181,12 @@ public:
void visit(const GnuDebugLinkSection &Sec) override;
void visit(const GroupSection &Sec) override;
void visit(const SectionIndexSection &Sec) override;
+ void visit(const CompressedSection &Sec) override;
+ void visit(const DecompressedSection &Sec) override;
explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {}
};
-// The class Buffer abstracts out the common interface of FileOutputBuffer and
-// WritableMemoryBuffer so that the hierarchy of Writers depends on this
-// abstract interface and doesn't depend on a particular implementation.
-// TODO: refactor the buffer classes in LLVM to enable us to use them here
-// directly.
-class Buffer {
- StringRef Name;
-
-public:
- virtual ~Buffer();
- virtual void allocate(size_t Size) = 0;
- virtual uint8_t *getBufferStart() = 0;
- virtual Error commit() = 0;
-
- explicit Buffer(StringRef Name) : Name(Name) {}
- StringRef getName() const { return Name; }
-};
-
-class FileBuffer : public Buffer {
- std::unique_ptr<FileOutputBuffer> Buf;
-
-public:
- void allocate(size_t Size) override;
- uint8_t *getBufferStart() override;
- Error commit() override;
-
- explicit FileBuffer(StringRef FileName) : Buffer(FileName) {}
-};
-
-class MemBuffer : public Buffer {
- std::unique_ptr<WritableMemoryBuffer> Buf;
-
-public:
- void allocate(size_t Size) override;
- uint8_t *getBufferStart() override;
- Error commit() override;
-
- explicit MemBuffer(StringRef Name) : Buffer(Name) {}
-
- std::unique_ptr<WritableMemoryBuffer> releaseMemoryBuffer();
-};
-
class Writer {
protected:
Object &Obj;
@@ -190,10 +202,13 @@ public:
template <class ELFT> class ELFWriter : public Writer {
private:
+ using Elf_Addr = typename ELFT::Addr;
using Elf_Shdr = typename ELFT::Shdr;
using Elf_Phdr = typename ELFT::Phdr;
using Elf_Ehdr = typename ELFT::Ehdr;
+ void initEhdrSegment();
+
void writeEhdr();
void writePhdr(const Segment &Seg);
void writeShdr(const SectionBase &Sec);
@@ -233,7 +248,7 @@ public:
class SectionBase {
public:
- StringRef Name;
+ std::string Name;
Segment *ParentSegment = nullptr;
uint64_t HeaderOffset;
uint64_t OriginalOffset = std::numeric_limits<uint64_t>::max();
@@ -250,6 +265,10 @@ public:
uint64_t Offset = 0;
uint64_t Size = 0;
uint64_t Type = ELF::SHT_NULL;
+ ArrayRef<uint8_t> OriginalData;
+
+ SectionBase() = default;
+ SectionBase(const SectionBase &) = default;
virtual ~SectionBase() = default;
@@ -258,6 +277,7 @@ public:
virtual void removeSectionReferences(const SectionBase *Sec);
virtual void removeSymbols(function_ref<bool(const Symbol &)> ToRemove);
virtual void accept(SectionVisitor &Visitor) const = 0;
+ virtual void accept(MutableSectionVisitor &Visitor) = 0;
virtual void markSymbols();
};
@@ -275,21 +295,21 @@ private:
};
std::set<const SectionBase *, SectionCompare> Sections;
- ArrayRef<uint8_t> Contents;
public:
- uint64_t Align;
- uint64_t FileSize;
+ uint32_t Type;
uint32_t Flags;
- uint32_t Index;
- uint64_t MemSize;
uint64_t Offset;
- uint64_t PAddr;
- uint64_t Type;
uint64_t VAddr;
+ uint64_t PAddr;
+ uint64_t FileSize;
+ uint64_t MemSize;
+ uint64_t Align;
+ uint32_t Index;
uint64_t OriginalOffset;
Segment *ParentSegment = nullptr;
+ ArrayRef<uint8_t> Contents;
explicit Segment(ArrayRef<uint8_t> Data) : Contents(Data) {}
Segment() {}
@@ -314,6 +334,7 @@ public:
explicit Section(ArrayRef<uint8_t> Data) : Contents(Data) {}
void accept(SectionVisitor &Visitor) const override;
+ void accept(MutableSectionVisitor &Visitor) override;
void removeSectionReferences(const SectionBase *Sec) override;
void initialize(SectionTableRef SecTable) override;
void finalize() override;
@@ -327,13 +348,57 @@ class OwnedDataSection : public SectionBase {
public:
OwnedDataSection(StringRef SecName, ArrayRef<uint8_t> Data)
: Data(std::begin(Data), std::end(Data)) {
- Name = SecName;
+ Name = SecName.str();
Type = ELF::SHT_PROGBITS;
Size = Data.size();
OriginalOffset = std::numeric_limits<uint64_t>::max();
}
void accept(SectionVisitor &Sec) const override;
+ void accept(MutableSectionVisitor &Visitor) override;
+};
+
+class CompressedSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+
+ DebugCompressionType CompressionType;
+ uint64_t DecompressedSize;
+ uint64_t DecompressedAlign;
+ SmallVector<char, 128> CompressedData;
+
+public:
+ CompressedSection(const SectionBase &Sec,
+ DebugCompressionType CompressionType);
+ CompressedSection(ArrayRef<uint8_t> CompressedData, uint64_t DecompressedSize,
+ uint64_t DecompressedAlign);
+
+ uint64_t getDecompressedSize() const { return DecompressedSize; }
+ uint64_t getDecompressedAlign() const { return DecompressedAlign; }
+
+ void accept(SectionVisitor &Visitor) const override;
+ void accept(MutableSectionVisitor &Visitor) override;
+
+ static bool classof(const SectionBase *S) {
+ return (S->Flags & ELF::SHF_COMPRESSED) ||
+ (StringRef(S->Name).startswith(".zdebug"));
+ }
+};
+
+class DecompressedSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+
+public:
+ explicit DecompressedSection(const CompressedSection &Sec)
+ : SectionBase(Sec) {
+ Size = Sec.getDecompressedSize();
+ Align = Sec.getDecompressedAlign();
+ Flags = (Flags & ~ELF::SHF_COMPRESSED);
+ if (StringRef(Name).startswith(".zdebug"))
+ Name = "." + Name.substr(2);
+ }
+
+ void accept(SectionVisitor &Visitor) const override;
+ void accept(MutableSectionVisitor &Visitor) override;
};
// There are two types of string tables that can exist, dynamic and not dynamic.
@@ -358,6 +423,7 @@ public:
uint32_t findIndex(StringRef Name) const;
void finalize() override;
void accept(SectionVisitor &Visitor) const override;
+ void accept(MutableSectionVisitor &Visitor) override;
static bool classof(const SectionBase *S) {
if (S->Flags & ELF::SHF_ALLOC)
@@ -386,7 +452,7 @@ struct Symbol {
SectionBase *DefinedIn = nullptr;
SymbolShndxType ShndxType;
uint32_t Index;
- StringRef Name;
+ std::string Name;
uint32_t NameIndex;
uint64_t Size;
uint8_t Type;
@@ -395,6 +461,7 @@ struct Symbol {
bool Referenced = false;
uint16_t getShndx() const;
+ bool isCommon() const;
};
class SectionIndexSection : public SectionBase {
@@ -414,6 +481,7 @@ public:
void initialize(SectionTableRef SecTable) override;
void finalize() override;
void accept(SectionVisitor &Visitor) const override;
+ void accept(MutableSectionVisitor &Visitor) override;
SectionIndexSection() {
Name = ".symtab_shndx";
@@ -437,9 +505,11 @@ protected:
using SymPtr = std::unique_ptr<Symbol>;
public:
- void addSymbol(StringRef Name, uint8_t Bind, uint8_t Type,
- SectionBase *DefinedIn, uint64_t Value, uint8_t Visibility,
- uint16_t Shndx, uint64_t Sz);
+ SymbolTableSection() { Type = ELF::SHT_SYMTAB; }
+
+ void addSymbol(Twine Name, uint8_t Bind, uint8_t Type, SectionBase *DefinedIn,
+ uint64_t Value, uint8_t Visibility, uint16_t Shndx,
+ uint64_t Size);
void prepareForLayout();
// An 'empty' symbol table still contains a null symbol.
bool empty() const { return Symbols.size() == 1; }
@@ -456,6 +526,7 @@ public:
void initialize(SectionTableRef SecTable) override;
void finalize() override;
void accept(SectionVisitor &Visitor) const override;
+ void accept(MutableSectionVisitor &Visitor) override;
void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override;
static bool classof(const SectionBase *S) {
@@ -517,6 +588,7 @@ class RelocationSection
public:
void addRelocation(Relocation Rel) { Relocations.push_back(Rel); }
void accept(SectionVisitor &Visitor) const override;
+ void accept(MutableSectionVisitor &Visitor) override;
void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override;
void markSymbols() override;
@@ -549,8 +621,8 @@ public:
void setFlagWord(ELF::Elf32_Word W) { FlagWord = W; }
void addMember(SectionBase *Sec) { GroupMembers.push_back(Sec); }
- void initialize(SectionTableRef SecTable) override{};
void accept(SectionVisitor &) const override;
+ void accept(MutableSectionVisitor &Visitor) override;
void finalize() override;
void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override;
void markSymbols() override;
@@ -589,6 +661,7 @@ public:
explicit DynamicRelocationSection(ArrayRef<uint8_t> Data) : Contents(Data) {}
void accept(SectionVisitor &) const override;
+ void accept(MutableSectionVisitor &Visitor) override;
static bool classof(const SectionBase *S) {
if (!(S->Flags & ELF::SHF_ALLOC))
@@ -610,6 +683,7 @@ public:
// If we add this section from an external source we can use this ctor.
explicit GnuDebugLinkSection(StringRef File);
void accept(SectionVisitor &Visitor) const override;
+ void accept(MutableSectionVisitor &Visitor) override;
};
class Reader {
@@ -623,11 +697,29 @@ using object::ELFFile;
using object::ELFObjectFile;
using object::OwningBinary;
+class BinaryELFBuilder {
+ uint16_t EMachine;
+ MemoryBuffer *MemBuf;
+ std::unique_ptr<Object> Obj;
+
+ void initFileHeader();
+ void initHeaderSegment();
+ StringTableSection *addStrTab();
+ SymbolTableSection *addSymTab(StringTableSection *StrTab);
+ void addData(SymbolTableSection *SymTab);
+ void initSections();
+
+public:
+ BinaryELFBuilder(uint16_t EM, MemoryBuffer *MB)
+ : EMachine(EM), MemBuf(MB), Obj(llvm::make_unique<Object>()) {}
+
+ std::unique_ptr<Object> build();
+};
+
template <class ELFT> class ELFBuilder {
private:
using Elf_Addr = typename ELFT::Addr;
using Elf_Shdr = typename ELFT::Shdr;
- using Elf_Ehdr = typename ELFT::Ehdr;
using Elf_Word = typename ELFT::Word;
const ELFFile<ELFT> &ElfFile;
@@ -647,13 +739,22 @@ public:
void build();
};
+class BinaryReader : public Reader {
+ const MachineInfo &MInfo;
+ MemoryBuffer *MemBuf;
+
+public:
+ BinaryReader(const MachineInfo &MI, MemoryBuffer *MB)
+ : MInfo(MI), MemBuf(MB) {}
+ std::unique_ptr<Object> create() const override;
+};
+
class ELFReader : public Reader {
Binary *Bin;
public:
- ElfType getElfType() const;
std::unique_ptr<Object> create() const override;
- explicit ELFReader(Binary *B) : Bin(B){};
+ explicit ELFReader(Binary *B) : Bin(B) {}
};
class Object {
@@ -682,7 +783,8 @@ public:
Segment ElfHdrSegment;
Segment ProgramHdrSegment;
- uint8_t Ident[16];
+ uint8_t OSABI;
+ uint8_t ABIVersion;
uint64_t Entry;
uint64_t SHOffset;
uint32_t Type;
@@ -708,6 +810,7 @@ public:
auto Sec = llvm::make_unique<T>(std::forward<Ts>(Args)...);
auto Ptr = Sec.get();
Sections.emplace_back(std::move(Sec));
+ Ptr->Index = Sections.size();
return *Ptr;
}
Segment &addSegment(ArrayRef<uint8_t> Data) {
@@ -715,6 +818,8 @@ public:
return *Segments.back();
}
};
+
+} // end namespace elf
} // end namespace objcopy
} // end namespace llvm
diff --git a/tools/llvm-objcopy/ObjcopyOpts.td b/tools/llvm-objcopy/ObjcopyOpts.td
index 2af2108d98d3..1f7e64e4091c 100644
--- a/tools/llvm-objcopy/ObjcopyOpts.td
+++ b/tools/llvm-objcopy/ObjcopyOpts.td
@@ -1,55 +1,98 @@
include "llvm/Option/OptParser.td"
-multiclass Eq<string name> {
- def NAME: Separate<["--", "-"], name>;
- def NAME # _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>;
+multiclass Eq<string name, string help> {
+ def NAME : Separate<["--", "-"], name>;
+ def NAME #_eq : Joined<["--", "-"], name #"=">,
+ Alias<!cast<Separate>(NAME)>,
+ HelpText<help>;
}
def help : Flag<["-", "--"], "help">;
-defm binary_architecture : Eq<"binary-architecture">,
- HelpText<"Used when transforming an architecture-less format (such as binary) to another format">;
-def B : JoinedOrSeparate<["-"], "B">,
- Alias<binary_architecture>;
-defm input_target : Eq<"input-target">,
- HelpText<"Format of the input file">,
+
+defm binary_architecture
+ : Eq<"binary-architecture", "Used when transforming an architecture-less "
+ "format (such as binary) to another format">;
+def B : JoinedOrSeparate<["-"], "B">, Alias<binary_architecture>;
+
+defm target : Eq<"target", "Format of the input and output file">,
+ Values<"binary">;
+def F : JoinedOrSeparate<["-"], "F">, Alias<target>;
+
+defm input_target : Eq<"input-target", "Format of the input file">,
Values<"binary">;
-defm output_target : Eq<"output-target">,
- HelpText<"Format of the output file">,
+def I : JoinedOrSeparate<["-"], "I">, Alias<input_target>;
+
+defm output_target : Eq<"output-target", "Format of the output file">,
Values<"binary">;
-def O : JoinedOrSeparate<["-"], "O">,
- Alias<output_target>;
-defm split_dwo : Eq<"split-dwo">,
- MetaVarName<"dwo-file">,
- HelpText<"Equivalent to extract-dwo on the input file to <dwo-file>, then strip-dwo on the input file">;
-defm add_gnu_debuglink : Eq<"add-gnu-debuglink">,
- MetaVarName<"debug-file">,
- HelpText<"Add a .gnu_debuglink for <debug-file>">;
-defm remove_section : Eq<"remove-section">,
- MetaVarName<"section">,
- HelpText<"Remove <section>">;
-defm rename_section : Eq<"rename-section">,
- MetaVarName<"old=new">,
- HelpText<"Renames a section from old to new">;
-defm redefine_symbol : Eq<"redefine-sym">,
- MetaVarName<"old=new">,
- HelpText<"Change the name of a symbol old to new">;
-def R : JoinedOrSeparate<["-"], "R">,
- Alias<remove_section>;
-defm keep : Eq<"keep">,
- MetaVarName<"section">,
- HelpText<"Keep <section>">;
-defm only_keep : Eq<"only-keep">,
- MetaVarName<"section">,
- HelpText<"Remove all but <section>">;
-def j : JoinedOrSeparate<["-"], "j">,
- Alias<only_keep>;
-defm add_section : Eq<"add-section">,
- MetaVarName<"section=file">,
- HelpText<"Make a section named <section> with the contents of <file>.">;
-def strip_all : Flag<["-", "--"], "strip-all">,
- HelpText<"Remove non-allocated sections other than .gnu.warning* sections">;
+def O : JoinedOrSeparate<["-"], "O">, Alias<output_target>;
+
+def compress_debug_sections : Flag<["--", "-"], "compress-debug-sections">;
+def compress_debug_sections_eq
+ : Joined<["--", "-"], "compress-debug-sections=">,
+ MetaVarName<"[ zlib | zlib-gnu ]">,
+ HelpText<"Compress DWARF debug sections using specified style. Supported "
+ "styles: 'zlib-gnu' and 'zlib'">;
+def decompress_debug_sections : Flag<["-", "--"], "decompress-debug-sections">,
+ HelpText<"Decompress DWARF debug sections.">;
+defm split_dwo
+ : Eq<"split-dwo", "Equivalent to extract-dwo on the input file to "
+ "<dwo-file>, then strip-dwo on the input file">,
+ MetaVarName<"dwo-file">;
+
+def enable_deterministic_archives
+ : Flag<["-", "--"], "enable-deterministic-archives">,
+ HelpText<"Enable deterministic mode when copying archives (use zero for "
+ "UIDs, GIDs, and timestamps).">;
+def D : Flag<["-"], "D">,
+ Alias<enable_deterministic_archives>,
+ HelpText<"Alias for --enable-deterministic-archives">;
+
+def disable_deterministic_archives
+ : Flag<["-", "--"], "disable-deterministic-archives">,
+ HelpText<"Disable deterministic mode when copying archives (use real "
+ "values for UIDs, GIDs, and timestamps).">;
+def U : Flag<["-"], "U">,
+ Alias<disable_deterministic_archives>,
+ HelpText<"Alias for --disable-deterministic-archives">;
+
+def preserve_dates : Flag<["-", "--"], "preserve-dates">,
+ HelpText<"Preserve access and modification timestamps">;
+def p : Flag<["-"], "p">, Alias<preserve_dates>;
+
+defm add_gnu_debuglink
+ : Eq<"add-gnu-debuglink", "Add a .gnu_debuglink for <debug-file>">,
+ MetaVarName<"debug-file">;
+
+defm remove_section : Eq<"remove-section", "Remove <section>">,
+ MetaVarName<"section">;
+def R : JoinedOrSeparate<["-"], "R">, Alias<remove_section>;
+
+defm rename_section
+ : Eq<"rename-section",
+ "Renames a section from old to new, optionally with specified flags. "
+ "Flags supported for GNU compatibility: alloc, load, noload, "
+ "readonly, debug, code, data, rom, share, contents, merge, strings.">,
+ MetaVarName<"old=new[,flag1,...]">;
+defm redefine_symbol
+ : Eq<"redefine-sym", "Change the name of a symbol old to new">,
+ MetaVarName<"old=new">;
+defm keep_section : Eq<"keep-section", "Keep <section>">,
+ MetaVarName<"section">;
+defm only_section : Eq<"only-section", "Remove all but <section>">,
+ MetaVarName<"section">;
+def j : JoinedOrSeparate<["-"], "j">, Alias<only_section>;
+defm add_section
+ : Eq<"add-section",
+ "Make a section named <section> with the contents of <file>.">,
+ MetaVarName<"section=file">;
+
+def strip_all
+ : Flag<["-", "--"], "strip-all">,
+ HelpText<
+ "Remove non-allocated sections other than .gnu.warning* sections">;
+def S : Flag<["-"], "S">, Alias<strip_all>;
def strip_all_gnu : Flag<["-", "--"], "strip-all-gnu">,
- HelpText<"Compaitable with GNU objcopy's --strip-all">;
+ HelpText<"Compatible with GNU objcopy's --strip-all">;
def strip_debug : Flag<["-", "--"], "strip-debug">,
HelpText<"Remove all debug information">;
def strip_dwo : Flag<["-", "--"], "strip-dwo">,
@@ -58,42 +101,80 @@ def strip_sections : Flag<["-", "--"], "strip-sections">,
HelpText<"Remove all section headers">;
def strip_non_alloc : Flag<["-", "--"], "strip-non-alloc">,
HelpText<"Remove all non-allocated sections">;
-def extract_dwo : Flag<["-", "--"], "extract-dwo">,
- HelpText<"Remove all sections that are not DWARF .dwo sections from file">;
-def localize_hidden : Flag<["-", "--"], "localize-hidden">,
- HelpText<"Mark all symbols that have hidden or internal visibility as local">;
-defm localize_symbol : Eq<"localize-symbol">,
- MetaVarName<"symbol">,
- HelpText<"Mark <symbol> as local">;
-def L : JoinedOrSeparate<["-"], "L">,
- Alias<localize_symbol>;
-defm globalize_symbol : Eq<"globalize-symbol">,
- MetaVarName<"symbol">,
- HelpText<"Mark <symbol> as global">;
-defm weaken_symbol : Eq<"weaken-symbol">,
- MetaVarName<"symbol">,
- HelpText<"Mark <symbol> as weak">;
-def W : JoinedOrSeparate<["-"], "W">,
- Alias<weaken_symbol>;
-def weaken : Flag<["-", "--"], "weaken">,
- HelpText<"Mark all global symbols as weak">;
-def discard_all : Flag<["-", "--"], "discard-all">,
- HelpText<"Remove all local symbols except file and section symbols">;
-def x : Flag<["-"], "x">,
- Alias<discard_all>;
-defm strip_symbol : Eq<"strip-symbol">,
- MetaVarName<"symbol">,
- HelpText<"Remove symbol <symbol>">;
-def N : JoinedOrSeparate<["-"], "N">,
- Alias<strip_symbol>;
-defm keep_symbol : Eq<"keep-symbol">,
- MetaVarName<"symbol">,
- HelpText<"Do not remove symbol <symbol>">;
-def K : JoinedOrSeparate<["-"], "K">,
- Alias<keep_symbol>;
-def only_keep_debug : Flag<["-", "--"], "only-keep-debug">,
- HelpText<"Currently ignored. Only for compaitability with GNU objcopy.">;
def strip_unneeded : Flag<["-", "--"], "strip-unneeded">,
- HelpText<"Remove all symbols not needed by relocations">;
+ HelpText<"Remove all symbols not needed by relocations">;
+
+def extract_dwo
+ : Flag<["-", "--"], "extract-dwo">,
+ HelpText<
+ "Remove all sections that are not DWARF .dwo sections from file">;
+
+def localize_hidden
+ : Flag<["-", "--"], "localize-hidden">,
+ HelpText<
+ "Mark all symbols that have hidden or internal visibility as local">;
+defm localize_symbol : Eq<"localize-symbol", "Mark <symbol> as local">,
+ MetaVarName<"symbol">;
+def L : JoinedOrSeparate<["-"], "L">, Alias<localize_symbol>;
+
+defm globalize_symbol : Eq<"globalize-symbol", "Mark <symbol> as global">,
+ MetaVarName<"symbol">;
+defm keep_global_symbol
+ : Eq<"keep-global-symbol",
+ "Convert all symbols except <symbol> to local. May be repeated to "
+ "convert all except a set of symbols to local.">,
+ MetaVarName<"symbol">;
+def G : JoinedOrSeparate<["-"], "G">, Alias<keep_global_symbol>;
+
+defm keep_global_symbols
+ : Eq<"keep-global-symbols",
+ "Reads a list of symbols from <filename> and runs as if "
+ "--keep-global-symbol=<symbol> is set for each one. <filename> "
+ "contains one symbol per line and may contain comments beginning with "
+ "'#'. Leading and trailing whitespace is stripped from each line. May "
+ "be repeated to read symbols from many files.">,
+ MetaVarName<"filename">;
+
+defm weaken_symbol : Eq<"weaken-symbol", "Mark <symbol> as weak">,
+ MetaVarName<"symbol">;
+def W : JoinedOrSeparate<["-"], "W">, Alias<weaken_symbol>;
+def weaken : Flag<["-", "--"], "weaken">,
+ HelpText<"Mark all global symbols as weak">;
+def discard_all
+ : Flag<["-", "--"], "discard-all">,
+ HelpText<"Remove all local symbols except file and section symbols">;
+def x : Flag<["-"], "x">, Alias<discard_all>;
+defm strip_symbol : Eq<"strip-symbol", "Remove symbol <symbol>">,
+ MetaVarName<"symbol">;
+def N : JoinedOrSeparate<["-"], "N">, Alias<strip_symbol>;
+defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">,
+ MetaVarName<"symbol">;
+def K : JoinedOrSeparate<["-"], "K">, Alias<keep_symbol>;
+def only_keep_debug
+ : Flag<["-", "--"], "only-keep-debug">,
+ HelpText<"Currently ignored. Only for compatibility with GNU objcopy.">;
def keep_file_symbols : Flag<["-", "--"], "keep-file-symbols">,
- HelpText<"Do not remove file symbols">;
+ HelpText<"Do not remove file symbols">;
+defm dump_section
+ : Eq<"dump-section",
+ "Dump contents of section named <section> into file <file>">,
+ MetaVarName<"section=file">;
+defm prefix_symbols
+ : Eq<"prefix-symbols", "Add <prefix> to the start of every symbol name">,
+ MetaVarName<"prefix">;
+
+def version : Flag<["-", "--"], "version">,
+ HelpText<"Print the version and exit.">;
+def V : Flag<["-"], "V">, Alias<version>;
+defm build_id_link_dir
+ : Eq<"build-id-link-dir", "Set directory for --build-id-link-input and "
+ "--build-id-link-output to <dir>">,
+ MetaVarName<"dir">;
+defm build_id_link_input
+ : Eq<"build-id-link-input", "Hard-link the input to <dir>/xx/xxx<suffix> "
+ "name derived from hex build ID">,
+ MetaVarName<"suffix">;
+defm build_id_link_output
+ : Eq<"build-id-link-output", "Hard-link the output to <dir>/xx/xxx<suffix> "
+ "name derived from hex build ID">,
+ MetaVarName<"suffix">;
diff --git a/tools/llvm-objcopy/StripOpts.td b/tools/llvm-objcopy/StripOpts.td
index 333b0d288efa..fa98e27e9321 100644
--- a/tools/llvm-objcopy/StripOpts.td
+++ b/tools/llvm-objcopy/StripOpts.td
@@ -1,49 +1,67 @@
include "llvm/Option/OptParser.td"
-multiclass Eq<string name> {
- def NAME: Separate<["--", "-"], name>;
- def NAME # _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>;
+multiclass Eq<string name, string help> {
+ def NAME : Separate<["--", "-"], name>;
+ def NAME #_eq : Joined<["--", "-"], name #"=">,
+ Alias<!cast<Separate>(NAME)>,
+ HelpText<help>;
}
def help : Flag<["-", "--"], "help">;
-defm output : Eq<"o">,
- MetaVarName<"output">,
- HelpText<"Write output to <file>">;
+def enable_deterministic_archives
+ : Flag<["-", "--"], "enable-deterministic-archives">,
+ HelpText<"Enable deterministic mode when stripping archives (use zero "
+ "for UIDs, GIDs, and timestamps).">;
+def D : Flag<["-"], "D">,
+ Alias<enable_deterministic_archives>,
+ HelpText<"Alias for --enable-deterministic-archives">;
-def strip_all : Flag<["-", "--"], "strip-all">,
- HelpText<"Remove non-allocated sections other than .gnu.warning* sections">;
+def disable_deterministic_archives
+ : Flag<["-", "--"], "disable-deterministic-archives">,
+ HelpText<"Disable deterministic mode when stripping archives (use real "
+ "values for UIDs, GIDs, and timestamps).">;
+def U : Flag<["-"], "U">,
+ Alias<disable_deterministic_archives>,
+ HelpText<"Alias for --disable-deterministic-archives">;
-def strip_debug : Flag<["-", "--"], "strip-debug">,
- HelpText<"Remove debugging symbols only">;
-
-def d : Flag<["-"], "d">,
- Alias<strip_debug>;
-
-def g : Flag<["-"], "g">,
- Alias<strip_debug>;
+defm output : Eq<"o", "Write output to <file>">, MetaVarName<"output">;
-def S : Flag<["-"], "S">,
- Alias<strip_debug>;
+def preserve_dates : Flag<["-", "--"], "preserve-dates">,
+ HelpText<"Preserve access and modification timestamps">;
+def p : Flag<["-"], "p">, Alias<preserve_dates>;
-defm remove_section : Eq<"remove-section">,
- MetaVarName<"section">,
- HelpText<"Remove <section>">;
+def strip_all
+ : Flag<["-", "--"], "strip-all">,
+ HelpText<
+ "Remove non-allocated sections other than .gnu.warning* sections">;
+def s : Flag<["-"], "s">, Alias<strip_all>;
-def R : JoinedOrSeparate<["-"], "R">,
- Alias<remove_section>;
+def strip_all_gnu : Flag<["-", "--"], "strip-all-gnu">,
+ HelpText<"Compatible with GNU strip's --strip-all">;
+def strip_debug : Flag<["-", "--"], "strip-debug">,
+ HelpText<"Remove debugging symbols only">;
+def d : Flag<["-"], "d">, Alias<strip_debug>;
+def g : Flag<["-"], "g">, Alias<strip_debug>;
+def S : Flag<["-"], "S">, Alias<strip_debug>;
+def strip_unneeded : Flag<["-", "--"], "strip-unneeded">,
+ HelpText<"Remove all symbols not needed by relocations">;
-defm keep_symbol : Eq<"keep-symbol">,
- MetaVarName<"symbol">,
- HelpText<"Do not remove symbol <symbol>">;
+defm remove_section : Eq<"remove-section", "Remove <section>">,
+ MetaVarName<"section">;
+def R : JoinedOrSeparate<["-"], "R">, Alias<remove_section>;
-def K : JoinedOrSeparate<["-"], "K">,
- Alias<keep_symbol>;
+defm keep_section : Eq<"keep-section", "Keep <section>">,
+ MetaVarName<"section">;
+defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">,
+ MetaVarName<"symbol">;
+def K : JoinedOrSeparate<["-"], "K">, Alias<keep_symbol>;
-def discard_all : Flag<["-", "--"], "discard-all">,
- HelpText<"Remove all local symbols except file and section symbols">;
-def x : Flag<["-"], "x">,
- Alias<discard_all>;
+def discard_all
+ : Flag<["-", "--"], "discard-all">,
+ HelpText<"Remove all local symbols except file and section symbols">;
+def x : Flag<["-"], "x">, Alias<discard_all>;
-def strip_unneeded : Flag<["-", "--"], "strip-unneeded">,
- HelpText<"Remove all symbols not needed by relocations">;
+def version : Flag<["-", "--"], "version">,
+ HelpText<"Print the version and exit.">;
+def V : Flag<["-"], "V">, Alias<version>;
diff --git a/tools/llvm-objcopy/llvm-objcopy.cpp b/tools/llvm-objcopy/llvm-objcopy.cpp
index 21a1622db765..fb1ff18b015b 100644
--- a/tools/llvm-objcopy/llvm-objcopy.cpp
+++ b/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -8,14 +8,19 @@
//===----------------------------------------------------------------------===//
#include "llvm-objcopy.h"
-#include "Object.h"
+#include "Buffer.h"
+#include "COFF/COFFObjcopy.h"
+#include "CopyConfig.h"
+#include "ELF/ELFObjcopy.h"
+
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
-#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/Binary.h"
+#include "llvm/Object/COFF.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/ELFTypes.h"
#include "llvm/Object/Error.h"
@@ -23,137 +28,23 @@
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Casting.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Compiler.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
-#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Memory.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstdlib>
-#include <functional>
-#include <iterator>
#include <memory>
#include <string>
#include <system_error>
#include <utility>
-using namespace llvm;
-using namespace llvm::objcopy;
-using namespace object;
-using namespace ELF;
-
-namespace {
-
-enum ObjcopyID {
- OBJCOPY_INVALID = 0, // This is not an option ID.
-#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
- HELPTEXT, METAVAR, VALUES) \
- OBJCOPY_##ID,
-#include "ObjcopyOpts.inc"
-#undef OPTION
-};
-
-#define PREFIX(NAME, VALUE) const char *const OBJCOPY_##NAME[] = VALUE;
-#include "ObjcopyOpts.inc"
-#undef PREFIX
-
-static const opt::OptTable::Info ObjcopyInfoTable[] = {
-#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
- HELPTEXT, METAVAR, VALUES) \
- {OBJCOPY_##PREFIX, \
- NAME, \
- HELPTEXT, \
- METAVAR, \
- OBJCOPY_##ID, \
- opt::Option::KIND##Class, \
- PARAM, \
- FLAGS, \
- OBJCOPY_##GROUP, \
- OBJCOPY_##ALIAS, \
- ALIASARGS, \
- VALUES},
-#include "ObjcopyOpts.inc"
-#undef OPTION
-};
-
-class ObjcopyOptTable : public opt::OptTable {
-public:
- ObjcopyOptTable() : OptTable(ObjcopyInfoTable, true) {}
-};
-
-enum StripID {
- STRIP_INVALID = 0, // This is not an option ID.
-#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
- HELPTEXT, METAVAR, VALUES) \
- STRIP_##ID,
-#include "StripOpts.inc"
-#undef OPTION
-};
-
-#define PREFIX(NAME, VALUE) const char *const STRIP_##NAME[] = VALUE;
-#include "StripOpts.inc"
-#undef PREFIX
-
-static const opt::OptTable::Info StripInfoTable[] = {
-#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
- HELPTEXT, METAVAR, VALUES) \
- {STRIP_##PREFIX, NAME, HELPTEXT, \
- METAVAR, STRIP_##ID, opt::Option::KIND##Class, \
- PARAM, FLAGS, STRIP_##GROUP, \
- STRIP_##ALIAS, ALIASARGS, VALUES},
-#include "StripOpts.inc"
-#undef OPTION
-};
-
-class StripOptTable : public opt::OptTable {
-public:
- StripOptTable() : OptTable(StripInfoTable, true) {}
-};
-
-struct CopyConfig {
- StringRef OutputFilename;
- StringRef InputFilename;
- StringRef OutputFormat;
- StringRef InputFormat;
- StringRef BinaryArch;
-
- StringRef SplitDWO;
- StringRef AddGnuDebugLink;
- std::vector<StringRef> ToRemove;
- std::vector<StringRef> Keep;
- std::vector<StringRef> OnlyKeep;
- std::vector<StringRef> AddSection;
- std::vector<StringRef> SymbolsToLocalize;
- std::vector<StringRef> SymbolsToGlobalize;
- std::vector<StringRef> SymbolsToWeaken;
- std::vector<StringRef> SymbolsToRemove;
- std::vector<StringRef> SymbolsToKeep;
- StringMap<StringRef> SectionsToRename;
- StringMap<StringRef> SymbolsToRename;
- bool StripAll = false;
- bool StripAllGNU = false;
- bool StripDebug = false;
- bool StripSections = false;
- bool StripNonAlloc = false;
- bool StripDWO = false;
- bool StripUnneeded = false;
- bool ExtractDWO = false;
- bool LocalizeHidden = false;
- bool Weaken = false;
- bool DiscardAll = false;
- bool OnlyKeepDebug = false;
- bool KeepFileSymbols = false;
-};
-
-using SectionPred = std::function<bool(const SectionBase &Sec)>;
-
-} // namespace
-
namespace llvm {
namespace objcopy {
@@ -161,14 +52,15 @@ namespace objcopy {
StringRef ToolName;
LLVM_ATTRIBUTE_NORETURN void error(Twine Message) {
- errs() << ToolName << ": " << Message << ".\n";
+ WithColor::error(errs(), ToolName) << Message << ".\n";
errs().flush();
exit(1);
}
LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) {
assert(EC);
- errs() << ToolName << ": '" << File << "': " << EC.message() << ".\n";
+ WithColor::error(errs(), ToolName)
+ << "'" << File << "': " << EC.message() << ".\n";
exit(1);
}
@@ -176,304 +68,18 @@ LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) {
assert(E);
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(std::move(E), OS, "");
+ logAllUnhandledErrors(std::move(E), OS);
OS.flush();
- errs() << ToolName << ": '" << File << "': " << Buf;
+ WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf;
exit(1);
}
} // end namespace objcopy
} // end namespace llvm
-static bool IsDebugSection(const SectionBase &Sec) {
- return Sec.Name.startswith(".debug") || Sec.Name.startswith(".zdebug") ||
- Sec.Name == ".gdb_index";
-}
-
-static bool IsDWOSection(const SectionBase &Sec) {
- return Sec.Name.endswith(".dwo");
-}
-
-static bool OnlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) {
- // We can't remove the section header string table.
- if (&Sec == Obj.SectionNames)
- return false;
- // Short of keeping the string table we want to keep everything that is a DWO
- // section and remove everything else.
- return !IsDWOSection(Sec);
-}
-
-static std::unique_ptr<Writer> CreateWriter(const CopyConfig &Config,
- Object &Obj, Buffer &Buf,
- ElfType OutputElfType) {
- if (Config.OutputFormat == "binary") {
- return llvm::make_unique<BinaryWriter>(Obj, Buf);
- }
- // Depending on the initial ELFT and OutputFormat we need a different Writer.
- switch (OutputElfType) {
- case ELFT_ELF32LE:
- return llvm::make_unique<ELFWriter<ELF32LE>>(Obj, Buf,
- !Config.StripSections);
- case ELFT_ELF64LE:
- return llvm::make_unique<ELFWriter<ELF64LE>>(Obj, Buf,
- !Config.StripSections);
- case ELFT_ELF32BE:
- return llvm::make_unique<ELFWriter<ELF32BE>>(Obj, Buf,
- !Config.StripSections);
- case ELFT_ELF64BE:
- return llvm::make_unique<ELFWriter<ELF64BE>>(Obj, Buf,
- !Config.StripSections);
- }
- llvm_unreachable("Invalid output format");
-}
-
-static void SplitDWOToFile(const CopyConfig &Config, const Reader &Reader,
- StringRef File, ElfType OutputElfType) {
- auto DWOFile = Reader.create();
- DWOFile->removeSections(
- [&](const SectionBase &Sec) { return OnlyKeepDWOPred(*DWOFile, Sec); });
- FileBuffer FB(File);
- auto Writer = CreateWriter(Config, *DWOFile, FB, OutputElfType);
- Writer->finalize();
- Writer->write();
-}
-
-// This function handles the high level operations of GNU objcopy including
-// handling command line options. It's important to outline certain properties
-// we expect to hold of the command line operations. Any operation that "keeps"
-// should keep regardless of a remove. Additionally any removal should respect
-// any previous removals. Lastly whether or not something is removed shouldn't
-// depend a) on the order the options occur in or b) on some opaque priority
-// system. The only priority is that keeps/copies overrule removes.
-static void HandleArgs(const CopyConfig &Config, Object &Obj,
- const Reader &Reader, ElfType OutputElfType) {
-
- if (!Config.SplitDWO.empty()) {
- SplitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType);
- }
-
- // TODO: update or remove symbols only if there is an option that affects
- // them.
- if (Obj.SymbolTable) {
- Obj.SymbolTable->updateSymbols([&](Symbol &Sym) {
- if ((Config.LocalizeHidden &&
- (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) ||
- (!Config.SymbolsToLocalize.empty() &&
- is_contained(Config.SymbolsToLocalize, Sym.Name)))
- Sym.Binding = STB_LOCAL;
-
- if (!Config.SymbolsToGlobalize.empty() &&
- is_contained(Config.SymbolsToGlobalize, Sym.Name))
- Sym.Binding = STB_GLOBAL;
-
- if (!Config.SymbolsToWeaken.empty() &&
- is_contained(Config.SymbolsToWeaken, Sym.Name) &&
- Sym.Binding == STB_GLOBAL)
- Sym.Binding = STB_WEAK;
-
- if (Config.Weaken && Sym.Binding == STB_GLOBAL &&
- Sym.getShndx() != SHN_UNDEF)
- Sym.Binding = STB_WEAK;
-
- const auto I = Config.SymbolsToRename.find(Sym.Name);
- if (I != Config.SymbolsToRename.end())
- Sym.Name = I->getValue();
- });
-
- // The purpose of this loop is to mark symbols referenced by sections
- // (like GroupSection or RelocationSection). This way, we know which
- // symbols are still 'needed' and wich are not.
- if (Config.StripUnneeded) {
- for (auto &Section : Obj.sections())
- Section.markSymbols();
- }
-
- Obj.removeSymbols([&](const Symbol &Sym) {
- if ((!Config.SymbolsToKeep.empty() &&
- is_contained(Config.SymbolsToKeep, Sym.Name)) ||
- (Config.KeepFileSymbols && Sym.Type == STT_FILE))
- return false;
-
- if (Config.DiscardAll && Sym.Binding == STB_LOCAL &&
- Sym.getShndx() != SHN_UNDEF && Sym.Type != STT_FILE &&
- Sym.Type != STT_SECTION)
- return true;
-
- if (Config.StripAll || Config.StripAllGNU)
- return true;
-
- if (!Config.SymbolsToRemove.empty() &&
- is_contained(Config.SymbolsToRemove, Sym.Name)) {
- return true;
- }
-
- if (Config.StripUnneeded && !Sym.Referenced &&
- (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) &&
- Sym.Type != STT_FILE && Sym.Type != STT_SECTION)
- return true;
-
- return false;
- });
- }
-
- SectionPred RemovePred = [](const SectionBase &) { return false; };
-
- // Removes:
- if (!Config.ToRemove.empty()) {
- RemovePred = [&Config](const SectionBase &Sec) {
- return find(Config.ToRemove, Sec.Name) != Config.ToRemove.end();
- };
- }
-
- if (Config.StripDWO || !Config.SplitDWO.empty())
- RemovePred = [RemovePred](const SectionBase &Sec) {
- return IsDWOSection(Sec) || RemovePred(Sec);
- };
-
- if (Config.ExtractDWO)
- RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
- return OnlyKeepDWOPred(Obj, Sec) || RemovePred(Sec);
- };
-
- if (Config.StripAllGNU)
- RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
- if (RemovePred(Sec))
- return true;
- if ((Sec.Flags & SHF_ALLOC) != 0)
- return false;
- if (&Sec == Obj.SectionNames)
- return false;
- switch (Sec.Type) {
- case SHT_SYMTAB:
- case SHT_REL:
- case SHT_RELA:
- case SHT_STRTAB:
- return true;
- }
- return IsDebugSection(Sec);
- };
-
- if (Config.StripSections) {
- RemovePred = [RemovePred](const SectionBase &Sec) {
- return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0;
- };
- }
-
- if (Config.StripDebug) {
- RemovePred = [RemovePred](const SectionBase &Sec) {
- return RemovePred(Sec) || IsDebugSection(Sec);
- };
- }
-
- if (Config.StripNonAlloc)
- RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
- if (RemovePred(Sec))
- return true;
- if (&Sec == Obj.SectionNames)
- return false;
- return (Sec.Flags & SHF_ALLOC) == 0;
- };
-
- if (Config.StripAll)
- RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
- if (RemovePred(Sec))
- return true;
- if (&Sec == Obj.SectionNames)
- return false;
- if (Sec.Name.startswith(".gnu.warning"))
- return false;
- return (Sec.Flags & SHF_ALLOC) == 0;
- };
-
- // Explicit copies:
- if (!Config.OnlyKeep.empty()) {
- RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) {
- // Explicitly keep these sections regardless of previous removes.
- if (find(Config.OnlyKeep, Sec.Name) != Config.OnlyKeep.end())
- return false;
-
- // Allow all implicit removes.
- if (RemovePred(Sec))
- return true;
-
- // Keep special sections.
- if (Obj.SectionNames == &Sec)
- return false;
- if (Obj.SymbolTable == &Sec ||
- (Obj.SymbolTable && Obj.SymbolTable->getStrTab() == &Sec))
- return false;
-
- // Remove everything else.
- return true;
- };
- }
-
- if (!Config.Keep.empty()) {
- RemovePred = [Config, RemovePred](const SectionBase &Sec) {
- // Explicitly keep these sections regardless of previous removes.
- if (find(Config.Keep, Sec.Name) != Config.Keep.end())
- return false;
- // Otherwise defer to RemovePred.
- return RemovePred(Sec);
- };
- }
-
- // This has to be the last predicate assignment.
- // If the option --keep-symbol has been specified
- // and at least one of those symbols is present
- // (equivalently, the updated symbol table is not empty)
- // the symbol table and the string table should not be removed.
- if ((!Config.SymbolsToKeep.empty() || Config.KeepFileSymbols) &&
- Obj.SymbolTable && !Obj.SymbolTable->empty()) {
- RemovePred = [&Obj, RemovePred](const SectionBase &Sec) {
- if (&Sec == Obj.SymbolTable || &Sec == Obj.SymbolTable->getStrTab())
- return false;
- return RemovePred(Sec);
- };
- }
-
- Obj.removeSections(RemovePred);
-
- if (!Config.SectionsToRename.empty()) {
- for (auto &Sec : Obj.sections()) {
- const auto Iter = Config.SectionsToRename.find(Sec.Name);
- if (Iter != Config.SectionsToRename.end())
- Sec.Name = Iter->second;
- }
- }
-
- if (!Config.AddSection.empty()) {
- for (const auto &Flag : Config.AddSection) {
- auto SecPair = Flag.split("=");
- auto SecName = SecPair.first;
- auto File = SecPair.second;
- auto BufOrErr = MemoryBuffer::getFile(File);
- if (!BufOrErr)
- reportError(File, BufOrErr.getError());
- auto Buf = std::move(*BufOrErr);
- auto BufPtr = reinterpret_cast<const uint8_t *>(Buf->getBufferStart());
- auto BufSize = Buf->getBufferSize();
- Obj.addSection<OwnedDataSection>(SecName,
- ArrayRef<uint8_t>(BufPtr, BufSize));
- }
- }
-
- if (!Config.AddGnuDebugLink.empty())
- Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink);
-}
-
-static void ExecuteElfObjcopyOnBinary(const CopyConfig &Config, Binary &Binary,
- Buffer &Out) {
- ELFReader Reader(&Binary);
- std::unique_ptr<Object> Obj = Reader.create();
-
- HandleArgs(Config, *Obj, Reader, Reader.getElfType());
-
- std::unique_ptr<Writer> Writer =
- CreateWriter(Config, *Obj, Out, Reader.getElfType());
- Writer->finalize();
- Writer->write();
-}
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::objcopy;
// For regular archives this function simply calls llvm::writeArchive,
// For thin archives it writes the archive file itself as well as its members.
@@ -504,22 +110,48 @@ static Error deepWriteArchive(StringRef ArcName,
return Error::success();
}
-static void ExecuteElfObjcopyOnArchive(const CopyConfig &Config, const Archive &Ar) {
+/// The function executeObjcopyOnRawBinary does the dispatch based on the format
+/// of the output specified by the command line options.
+static void executeObjcopyOnRawBinary(const CopyConfig &Config,
+ MemoryBuffer &In, Buffer &Out) {
+ // TODO: llvm-objcopy should parse CopyConfig.OutputFormat to recognize
+ // formats other than ELF / "binary" and invoke
+ // elf::executeObjcopyOnRawBinary, macho::executeObjcopyOnRawBinary or
+ // coff::executeObjcopyOnRawBinary accordingly.
+ return elf::executeObjcopyOnRawBinary(Config, In, Out);
+}
+
+/// The function executeObjcopyOnBinary does the dispatch based on the format
+/// of the input binary (ELF, MachO or COFF).
+static void executeObjcopyOnBinary(const CopyConfig &Config, object::Binary &In,
+ Buffer &Out) {
+ if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In))
+ return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out);
+ else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In))
+ return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out);
+ else
+ error("Unsupported object file format");
+}
+
+static void executeObjcopyOnArchive(const CopyConfig &Config,
+ const Archive &Ar) {
std::vector<NewArchiveMember> NewArchiveMembers;
Error Err = Error::success();
for (const Archive::Child &Child : Ar.children(Err)) {
Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
if (!ChildOrErr)
reportError(Ar.getFileName(), ChildOrErr.takeError());
+ Binary *Bin = ChildOrErr->get();
+
Expected<StringRef> ChildNameOrErr = Child.getName();
if (!ChildNameOrErr)
reportError(Ar.getFileName(), ChildNameOrErr.takeError());
MemBuffer MB(ChildNameOrErr.get());
- ExecuteElfObjcopyOnBinary(Config, **ChildOrErr, MB);
+ executeObjcopyOnBinary(Config, *Bin, MB);
Expected<NewArchiveMember> Member =
- NewArchiveMember::getOldMember(Child, true);
+ NewArchiveMember::getOldMember(Child, Config.DeterministicArchives);
if (!Member)
reportError(Ar.getFileName(), Member.takeError());
Member->Buf = MB.releaseMemoryBuffer();
@@ -529,180 +161,72 @@ static void ExecuteElfObjcopyOnArchive(const CopyConfig &Config, const Archive &
if (Err)
reportError(Config.InputFilename, std::move(Err));
- if (Error E =
- deepWriteArchive(Config.OutputFilename, NewArchiveMembers,
- Ar.hasSymbolTable(), Ar.kind(), true, Ar.isThin()))
+ if (Error E = deepWriteArchive(Config.OutputFilename, NewArchiveMembers,
+ Ar.hasSymbolTable(), Ar.kind(),
+ Config.DeterministicArchives, Ar.isThin()))
reportError(Config.OutputFilename, std::move(E));
}
-static void ExecuteElfObjcopy(const CopyConfig &Config) {
- Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr =
- createBinary(Config.InputFilename);
- if (!BinaryOrErr)
- reportError(Config.InputFilename, BinaryOrErr.takeError());
-
- if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary()))
- return ExecuteElfObjcopyOnArchive(Config, *Ar);
-
- FileBuffer FB(Config.OutputFilename);
- ExecuteElfObjcopyOnBinary(Config, *BinaryOrErr.get().getBinary(), FB);
-}
-
-// ParseObjcopyOptions returns the config and sets the input arguments. If a
-// help flag is set then ParseObjcopyOptions will print the help messege and
-// exit.
-static CopyConfig ParseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
- ObjcopyOptTable T;
- unsigned MissingArgumentIndex, MissingArgumentCount;
- llvm::opt::InputArgList InputArgs =
- T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
-
- if (InputArgs.size() == 0) {
- T.PrintHelp(errs(), "llvm-objcopy <input> [ <output> ]", "objcopy tool");
- exit(1);
- }
-
- if (InputArgs.hasArg(OBJCOPY_help)) {
- T.PrintHelp(outs(), "llvm-objcopy <input> [ <output> ]", "objcopy tool");
- exit(0);
- }
-
- SmallVector<const char *, 2> Positional;
-
- for (auto Arg : InputArgs.filtered(OBJCOPY_UNKNOWN))
- error("unknown argument '" + Arg->getAsString(InputArgs) + "'");
-
- for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT))
- Positional.push_back(Arg->getValue());
-
- if (Positional.empty())
- error("No input file specified");
-
- if (Positional.size() > 2)
- error("Too many positional arguments");
-
- CopyConfig Config;
- Config.InputFilename = Positional[0];
- Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1];
- Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target);
- Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target);
- Config.BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture);
-
- Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo);
- Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink);
-
- for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) {
- if (!StringRef(Arg->getValue()).contains('='))
- error("Bad format for --redefine-sym");
- auto Old2New = StringRef(Arg->getValue()).split('=');
- if (!Config.SymbolsToRename.insert(Old2New).second)
- error("Multiple redefinition of symbol " + Old2New.first);
- }
-
- for (auto Arg : InputArgs.filtered(OBJCOPY_rename_section)) {
- if (!StringRef(Arg->getValue()).contains('='))
- error("Bad format for --rename-section");
- auto Old2New = StringRef(Arg->getValue()).split('=');
- if (!Config.SectionsToRename.insert(Old2New).second)
- error("Already have a section rename for " + Old2New.first);
- }
-
- for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section))
- Config.ToRemove.push_back(Arg->getValue());
- for (auto Arg : InputArgs.filtered(OBJCOPY_keep))
- Config.Keep.push_back(Arg->getValue());
- for (auto Arg : InputArgs.filtered(OBJCOPY_only_keep))
- Config.OnlyKeep.push_back(Arg->getValue());
- for (auto Arg : InputArgs.filtered(OBJCOPY_add_section))
- Config.AddSection.push_back(Arg->getValue());
- Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all);
- Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu);
- Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug);
- Config.StripDWO = InputArgs.hasArg(OBJCOPY_strip_dwo);
- Config.StripSections = InputArgs.hasArg(OBJCOPY_strip_sections);
- Config.StripNonAlloc = InputArgs.hasArg(OBJCOPY_strip_non_alloc);
- Config.StripUnneeded = InputArgs.hasArg(OBJCOPY_strip_unneeded);
- Config.ExtractDWO = InputArgs.hasArg(OBJCOPY_extract_dwo);
- Config.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden);
- Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken);
- Config.DiscardAll = InputArgs.hasArg(OBJCOPY_discard_all);
- Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug);
- Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols);
- for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol))
- Config.SymbolsToLocalize.push_back(Arg->getValue());
- for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol))
- Config.SymbolsToGlobalize.push_back(Arg->getValue());
- for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol))
- Config.SymbolsToWeaken.push_back(Arg->getValue());
- for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol))
- Config.SymbolsToRemove.push_back(Arg->getValue());
- for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol))
- Config.SymbolsToKeep.push_back(Arg->getValue());
-
- return Config;
-}
-
-// ParseStripOptions returns the config and sets the input arguments. If a
-// help flag is set then ParseStripOptions will print the help messege and
-// exit.
-static CopyConfig ParseStripOptions(ArrayRef<const char *> ArgsArr) {
- StripOptTable T;
- unsigned MissingArgumentIndex, MissingArgumentCount;
- llvm::opt::InputArgList InputArgs =
- T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
-
- if (InputArgs.size() == 0) {
- T.PrintHelp(errs(), "llvm-strip <input> [ <output> ]", "strip tool");
- exit(1);
+static void restoreDateOnFile(StringRef Filename,
+ const sys::fs::file_status &Stat) {
+ int FD;
+
+ if (auto EC =
+ sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting))
+ reportError(Filename, EC);
+
+ if (auto EC = sys::fs::setLastAccessAndModificationTime(
+ FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime()))
+ reportError(Filename, EC);
+
+ if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD))
+ reportError(Filename, EC);
+}
+
+/// The function executeObjcopy does the higher level dispatch based on the type
+/// of input (raw binary, archive or single object file) and takes care of the
+/// format-agnostic modifications, i.e. preserving dates.
+static void executeObjcopy(const CopyConfig &Config) {
+ sys::fs::file_status Stat;
+ if (Config.PreserveDates)
+ if (auto EC = sys::fs::status(Config.InputFilename, Stat))
+ reportError(Config.InputFilename, EC);
+
+ if (Config.InputFormat == "binary") {
+ auto BufOrErr = MemoryBuffer::getFile(Config.InputFilename);
+ if (!BufOrErr)
+ reportError(Config.InputFilename, BufOrErr.getError());
+ FileBuffer FB(Config.OutputFilename);
+ executeObjcopyOnRawBinary(Config, *BufOrErr->get(), FB);
+ } else {
+ Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr =
+ createBinary(Config.InputFilename);
+ if (!BinaryOrErr)
+ reportError(Config.InputFilename, BinaryOrErr.takeError());
+
+ if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) {
+ executeObjcopyOnArchive(Config, *Ar);
+ } else {
+ FileBuffer FB(Config.OutputFilename);
+ executeObjcopyOnBinary(Config, *BinaryOrErr.get().getBinary(), FB);
+ }
}
- if (InputArgs.hasArg(STRIP_help)) {
- T.PrintHelp(outs(), "llvm-strip <input> [ <output> ]", "strip tool");
- exit(0);
+ if (Config.PreserveDates) {
+ restoreDateOnFile(Config.OutputFilename, Stat);
+ if (!Config.SplitDWO.empty())
+ restoreDateOnFile(Config.SplitDWO, Stat);
}
-
- SmallVector<const char *, 2> Positional;
- for (auto Arg : InputArgs.filtered(STRIP_UNKNOWN))
- error("unknown argument '" + Arg->getAsString(InputArgs) + "'");
- for (auto Arg : InputArgs.filtered(STRIP_INPUT))
- Positional.push_back(Arg->getValue());
-
- if (Positional.empty())
- error("No input file specified");
-
- if (Positional.size() > 2)
- error("Support for multiple input files is not implemented yet");
-
- CopyConfig Config;
- Config.InputFilename = Positional[0];
- Config.OutputFilename =
- InputArgs.getLastArgValue(STRIP_output, Positional[0]);
-
- Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug);
-
- Config.DiscardAll = InputArgs.hasArg(STRIP_discard_all);
- Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded);
- Config.StripAll = InputArgs.hasArg(STRIP_strip_all);
-
- if (!Config.StripDebug && !Config.StripUnneeded && !Config.DiscardAll)
- Config.StripAll = true;
-
- for (auto Arg : InputArgs.filtered(STRIP_remove_section))
- Config.ToRemove.push_back(Arg->getValue());
-
- for (auto Arg : InputArgs.filtered(STRIP_keep_symbol))
- Config.SymbolsToKeep.push_back(Arg->getValue());
-
- return Config;
}
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
ToolName = argv[0];
- CopyConfig Config;
- if (sys::path::stem(ToolName).endswith_lower("strip"))
- Config = ParseStripOptions(makeArrayRef(argv + 1, argc));
+ DriverConfig DriverConfig;
+ if (sys::path::stem(ToolName).contains("strip"))
+ DriverConfig = parseStripOptions(makeArrayRef(argv + 1, argc));
else
- Config = ParseObjcopyOptions(makeArrayRef(argv + 1, argc));
- ExecuteElfObjcopy(Config);
+ DriverConfig = parseObjcopyOptions(makeArrayRef(argv + 1, argc));
+ for (const CopyConfig &CopyConfig : DriverConfig.CopyConfigs)
+ executeObjcopy(CopyConfig);
}
diff --git a/tools/llvm-objcopy/llvm-objcopy.h b/tools/llvm-objcopy/llvm-objcopy.h
index e222b65dc78f..d8edf3e29ee0 100644
--- a/tools/llvm-objcopy/llvm-objcopy.h
+++ b/tools/llvm-objcopy/llvm-objcopy.h
@@ -31,7 +31,7 @@ template <class T> T unwrapOrError(Expected<T> EO) {
return *EO;
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(EO.takeError(), OS, "");
+ logAllUnhandledErrors(EO.takeError(), OS);
OS.flush();
error(Buf);
}
diff --git a/tools/llvm-objdump/COFFDump.cpp b/tools/llvm-objdump/COFFDump.cpp
index 7ca5d04593ff..55607ec299be 100644
--- a/tools/llvm-objdump/COFFDump.cpp
+++ b/tools/llvm-objdump/COFFDump.cpp
@@ -16,11 +16,13 @@
//===----------------------------------------------------------------------===//
#include "llvm-objdump.h"
+#include "llvm/Demangle/Demangle.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Win64EH.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@@ -453,7 +455,7 @@ static bool getPDataSection(const COFFObjectFile *Obj,
Rels.push_back(Reloc);
// Sort relocations by address.
- llvm::sort(Rels.begin(), Rels.end(), RelocAddressLess);
+ llvm::sort(Rels, isRelocAddressLess);
ArrayRef<uint8_t> Contents;
error(Obj->getSectionContents(Pdata, Contents));
@@ -578,8 +580,9 @@ static void printRuntimeFunctionRels(const COFFObjectFile *Obj,
void llvm::printCOFFUnwindInfo(const COFFObjectFile *Obj) {
if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_AMD64) {
- errs() << "Unsupported image machine type "
- "(currently only AMD64 is supported).\n";
+ WithColor::error(errs(), "llvm-objdump")
+ << "unsupported image machine type "
+ "(currently only AMD64 is supported).\n";
return;
}
@@ -646,10 +649,26 @@ void llvm::printCOFFSymbolTable(const COFFObjectFile *coff) {
<< "(sec " << format("%2d", int(Symbol->getSectionNumber())) << ")"
<< "(fl 0x00)" // Flag bits, which COFF doesn't have.
<< "(ty " << format("%3x", unsigned(Symbol->getType())) << ")"
- << "(scl " << format("%3x", unsigned(Symbol->getStorageClass())) << ") "
+ << "(scl " << format("%3x", unsigned(Symbol->getStorageClass()))
+ << ") "
<< "(nx " << unsigned(Symbol->getNumberOfAuxSymbols()) << ") "
<< "0x" << format("%08x", unsigned(Symbol->getValue())) << " "
- << Name << "\n";
+ << Name;
+ if (Demangle && Name.startswith("?")) {
+ char *DemangledSymbol = nullptr;
+ size_t Size = 0;
+ int Status = -1;
+ DemangledSymbol =
+ microsoftDemangle(Name.data(), DemangledSymbol, &Size, &Status);
+
+ if (Status == 0 && DemangledSymbol) {
+ outs() << " (" << StringRef(DemangledSymbol) << ")";
+ std::free(DemangledSymbol);
+ } else {
+ outs() << " (invalid mangled name)";
+ }
+ }
+ outs() << "\n";
for (unsigned AI = 0, AE = Symbol->getNumberOfAuxSymbols(); AI < AE; ++AI, ++SI) {
if (Symbol->isSectionDefinition()) {
diff --git a/tools/llvm-objdump/ELFDump.cpp b/tools/llvm-objdump/ELFDump.cpp
index f4d36656a6c4..b17a15a0d8fc 100644
--- a/tools/llvm-objdump/ELFDump.cpp
+++ b/tools/llvm-objdump/ELFDump.cpp
@@ -158,37 +158,23 @@ template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) {
}
void llvm::printELFFileHeader(const object::ObjectFile *Obj) {
- // Little-endian 32-bit
- if (const ELF32LEObjectFile *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
+ if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
printProgramHeaders(ELFObj->getELFFile());
-
- // Big-endian 32-bit
- if (const ELF32BEObjectFile *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
+ else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
printProgramHeaders(ELFObj->getELFFile());
-
- // Little-endian 64-bit
- if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
+ else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
printProgramHeaders(ELFObj->getELFFile());
-
- // Big-endian 64-bit
- if (const ELF64BEObjectFile *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
+ else if (const auto *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
printProgramHeaders(ELFObj->getELFFile());
}
void llvm::printELFDynamicSection(const object::ObjectFile *Obj) {
- // Little-endian 32-bit
- if (const ELF32LEObjectFile *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
+ if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
-
- // Big-endian 32-bit
- if (const ELF32BEObjectFile *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
+ else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
-
- // Little-endian 64-bit
- if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
+ else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
-
- // Big-endian 64-bit
- if (const ELF64BEObjectFile *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
+ else if (const auto *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
}
diff --git a/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp
index bdf80c73b999..5ef7058ec9da 100644
--- a/tools/llvm-objdump/MachODump.cpp
+++ b/tools/llvm-objdump/MachODump.cpp
@@ -44,6 +44,7 @@
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstring>
@@ -166,7 +167,7 @@ static const Target *GetTarget(const MachOObjectFile *MachOObj,
if (*ThumbTarget)
return TheTarget;
- errs() << "llvm-objdump: error: unable to get target for '";
+ WithColor::error(errs(), "llvm-objdump") << "unable to get target for '";
if (!TheTarget)
errs() << TripleName;
else
@@ -483,7 +484,7 @@ static void PrintRType(const uint64_t cputype, const unsigned r_type) {
"GOTLDP ", "GOTLDPOF", "PTRTGOT ", "TLVLDP ", "TLVLDPOF",
"ADDEND ", " 11 (?) ", " 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) "
};
-
+
if (r_type > 0xf){
outs() << format("%-7u", r_type) << " ";
return;
@@ -552,7 +553,7 @@ static void PrintRelocationEntries(const MachOObjectFile *O,
bool previous_arm_half = false;
bool previous_sectdiff = false;
uint32_t sectdiff_r_type = 0;
-
+
for (relocation_iterator Reloc = Begin; Reloc != End; ++Reloc) {
const DataRefImpl Rel = Reloc->getRawDataRefImpl();
const MachO::any_relocation_info RE = O->getRelocation(Rel);
@@ -567,7 +568,7 @@ static void PrintRelocationEntries(const MachOObjectFile *O,
O->getScatteredRelocationValue(RE) : 0);
const unsigned r_symbolnum = (r_scattered ? 0 :
O->getPlainRelocationSymbolNum(RE));
-
+
if (r_scattered && cputype != MachO::CPU_TYPE_X86_64) {
if (verbose) {
// scattered: address
@@ -578,20 +579,20 @@ static void PrintRelocationEntries(const MachOObjectFile *O,
outs() << " ";
else
outs() << format("%08x ", (unsigned int)r_address);
-
+
// scattered: pcrel
if (r_pcrel)
outs() << "True ";
else
outs() << "False ";
-
+
// scattered: length
PrintRLength(cputype, r_type, r_length, previous_arm_half);
-
+
// scattered: extern & type
outs() << "n/a ";
PrintRType(cputype, r_type);
-
+
// scattered: scattered & value
outs() << format("True 0x%08x", (unsigned int)r_value);
if (previous_sectdiff == false) {
@@ -639,22 +640,22 @@ static void PrintRelocationEntries(const MachOObjectFile *O,
outs() << " ";
else
outs() << format("%08x ", (unsigned int)r_address);
-
+
// plain: pcrel
if (r_pcrel)
outs() << "True ";
else
outs() << "False ";
-
+
// plain: length
PrintRLength(cputype, r_type, r_length, previous_arm_half);
-
+
if (r_extern) {
// plain: extern & type & scattered
outs() << "True ";
PrintRType(cputype, r_type);
outs() << "False ";
-
+
// plain: symbolnum/value
if (r_symbolnum > Symtab.nsyms)
outs() << format("?(%d)\n", r_symbolnum);
@@ -675,7 +676,7 @@ static void PrintRelocationEntries(const MachOObjectFile *O,
outs() << "False ";
PrintRType(cputype, r_type);
outs() << "False ";
-
+
// plain: symbolnum/value
if (cputype == MachO::CPU_TYPE_ARM &&
r_type == llvm::MachO::ARM_RELOC_PAIR)
@@ -1411,7 +1412,7 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
std::pair<StringRef, StringRef> DumpSegSectName;
DumpSegSectName = DumpSection.split(',');
StringRef DumpSegName, DumpSectName;
- if (DumpSegSectName.second.size()) {
+ if (!DumpSegSectName.second.empty()) {
DumpSegName = DumpSegSectName.first;
DumpSectName = DumpSegSectName.second;
} else {
@@ -1559,7 +1560,8 @@ static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) {
if (none_of(ArchFlags, [&](const std::string &Name) {
return Name == ArchFlagName;
})) {
- errs() << "llvm-objdump: " + Filename + ": No architecture specified.\n";
+ WithColor::error(errs(), "llvm-objdump")
+ << Filename << ": no architecture specified.\n";
return false;
}
return true;
@@ -1580,7 +1582,7 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF,
if (Disassemble || Relocations || PrivateHeaders || ExportsTrie || Rebase ||
Bind || SymbolTable || LazyBind || WeakBind || IndirectSymbols ||
DataInCode || LinkOptHints || DylibsUsed || DylibId || ObjcMetaData ||
- (FilterSections.size() != 0)) {
+ (!FilterSections.empty())) {
if (!NoLeadingHeaders) {
outs() << Name;
if (!ArchiveMemberName.empty())
@@ -1605,12 +1607,22 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF,
// If we need the symbol table to do the operation then check it here to
// produce a good error message as to where the Mach-O file comes from in
// the error message.
- if (Disassemble || IndirectSymbols || FilterSections.size() != 0 ||
- UnwindInfo)
+ if (Disassemble || IndirectSymbols || !FilterSections.empty() || UnwindInfo)
if (Error Err = MachOOF->checkSymbolTable())
report_error(ArchiveName, FileName, std::move(Err), ArchitectureName);
-
- if (Disassemble) {
+
+ if (DisassembleAll) {
+ for (const SectionRef &Section : MachOOF->sections()) {
+ StringRef SectName;
+ Section.getName(SectName);
+ if (SectName.equals("__text")) {
+ DataRefImpl Ref = Section.getRawDataRefImpl();
+ StringRef SegName = MachOOF->getSectionFinalSegmentName(Ref);
+ DisassembleMachO(FileName, MachOOF, SegName, SectName);
+ }
+ }
+ }
+ else if (Disassemble) {
if (MachOOF->getHeader().filetype == MachO::MH_KEXT_BUNDLE &&
MachOOF->getHeader().cputype == MachO::CPU_TYPE_ARM64)
DisassembleMachO(FileName, MachOOF, "__TEXT_EXEC", "__text");
@@ -1626,10 +1638,10 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF,
if (Relocations)
PrintRelocations(MachOOF, !NonVerbose);
if (SectionHeaders)
- PrintSectionHeaders(MachOOF);
+ printSectionHeaders(MachOOF);
if (SectionContents)
- PrintSectionContents(MachOOF);
- if (FilterSections.size() != 0)
+ printSectionContents(MachOOF);
+ if (!FilterSections.empty())
DumpSectionContents(FileName, MachOOF, !NonVerbose);
if (InfoPlist)
DumpInfoPlistSectionContents(FileName, MachOOF);
@@ -1638,7 +1650,7 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF,
if (DylibId)
PrintDylibs(MachOOF, true);
if (SymbolTable)
- PrintSymbolTable(MachOOF, ArchiveName, ArchitectureName);
+ printSymbolTable(MachOOF, ArchiveName, ArchitectureName);
if (UnwindInfo)
printMachOUnwindInfo(MachOOF);
if (PrivateHeaders) {
@@ -1937,23 +1949,30 @@ static void printArchiveHeaders(StringRef Filename, Archive *A, bool verbose,
report_error(StringRef(), Filename, std::move(Err), ArchitectureName);
}
-// ParseInputMachO() parses the named Mach-O file in Filename and handles the
-// -arch flags selecting just those slices as specified by them and also parses
-// archive files. Then for each individual Mach-O file ProcessMachO() is
-// called to process the file based on the command line options.
-void llvm::ParseInputMachO(StringRef Filename) {
+static bool ValidateArchFlags() {
// Check for -arch all and verifiy the -arch flags are valid.
for (unsigned i = 0; i < ArchFlags.size(); ++i) {
if (ArchFlags[i] == "all") {
ArchAll = true;
} else {
if (!MachOObjectFile::isValidArch(ArchFlags[i])) {
- errs() << "llvm-objdump: Unknown architecture named '" + ArchFlags[i] +
- "'for the -arch option\n";
- return;
+ WithColor::error(errs(), "llvm-objdump")
+ << "unknown architecture named '" + ArchFlags[i] +
+ "'for the -arch option\n";
+ return false;
}
}
}
+ return true;
+}
+
+// ParseInputMachO() parses the named Mach-O file in Filename and handles the
+// -arch flags selecting just those slices as specified by them and also parses
+// archive files. Then for each individual Mach-O file ProcessMachO() is
+// called to process the file based on the command line options.
+void llvm::parseInputMachO(StringRef Filename) {
+ if (!ValidateArchFlags())
+ return;
// Attempt to open the binary.
Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Filename);
@@ -1989,191 +2008,199 @@ void llvm::ParseInputMachO(StringRef Filename) {
report_error(Filename, std::move(Err));
return;
}
- if (UniversalHeaders) {
- if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin))
- printMachOUniversalHeaders(UB, !NonVerbose);
- }
if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) {
- // If we have a list of architecture flags specified dump only those.
- if (!ArchAll && ArchFlags.size() != 0) {
- // Look for a slice in the universal binary that matches each ArchFlag.
- bool ArchFound;
- for (unsigned i = 0; i < ArchFlags.size(); ++i) {
- ArchFound = false;
- for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
- E = UB->end_objects();
- I != E; ++I) {
- if (ArchFlags[i] == I->getArchFlagName()) {
- ArchFound = true;
- Expected<std::unique_ptr<ObjectFile>> ObjOrErr =
- I->getAsObjectFile();
- std::string ArchitectureName = "";
- if (ArchFlags.size() > 1)
- ArchitectureName = I->getArchFlagName();
- if (ObjOrErr) {
- ObjectFile &O = *ObjOrErr.get();
- if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O))
- ProcessMachO(Filename, MachOOF, "", ArchitectureName);
- } else if (auto E = isNotObjectErrorInvalidFileType(
- ObjOrErr.takeError())) {
- report_error(Filename, StringRef(), std::move(E),
- ArchitectureName);
- continue;
- } else if (Expected<std::unique_ptr<Archive>> AOrErr =
- I->getAsArchive()) {
- std::unique_ptr<Archive> &A = *AOrErr;
- outs() << "Archive : " << Filename;
- if (!ArchitectureName.empty())
- outs() << " (architecture " << ArchitectureName << ")";
- outs() << "\n";
- if (ArchiveHeaders)
- printArchiveHeaders(Filename, A.get(), !NonVerbose,
- ArchiveMemberOffsets, ArchitectureName);
- Error Err = Error::success();
- for (auto &C : A->children(Err)) {
- Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
- if (!ChildOrErr) {
- if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
- report_error(Filename, C, std::move(E), ArchitectureName);
- continue;
- }
- if (MachOObjectFile *O =
- dyn_cast<MachOObjectFile>(&*ChildOrErr.get()))
- ProcessMachO(Filename, O, O->getFileName(), ArchitectureName);
- }
- if (Err)
- report_error(Filename, std::move(Err));
- } else {
- consumeError(AOrErr.takeError());
- error("Mach-O universal file: " + Filename + " for " +
- "architecture " + StringRef(I->getArchFlagName()) +
- " is not a Mach-O file or an archive file");
- }
- }
- }
- if (!ArchFound) {
- errs() << "llvm-objdump: file: " + Filename + " does not contain "
- << "architecture: " + ArchFlags[i] + "\n";
- return;
- }
- }
+ parseInputMachO(UB);
+ return;
+ }
+ if (ObjectFile *O = dyn_cast<ObjectFile>(&Bin)) {
+ if (!checkMachOAndArchFlags(O, Filename))
return;
- }
- // No architecture flags were specified so if this contains a slice that
- // matches the host architecture dump only that.
- if (!ArchAll) {
+ if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&*O))
+ ProcessMachO(Filename, MachOOF);
+ else
+ WithColor::error(errs(), "llvm-objdump")
+ << Filename << "': "
+ << "object is not a Mach-O file type.\n";
+ return;
+ }
+ llvm_unreachable("Input object can't be invalid at this point");
+}
+
+void llvm::parseInputMachO(MachOUniversalBinary *UB) {
+ if (!ValidateArchFlags())
+ return;
+
+ auto Filename = UB->getFileName();
+
+ if (UniversalHeaders)
+ printMachOUniversalHeaders(UB, !NonVerbose);
+
+ // If we have a list of architecture flags specified dump only those.
+ if (!ArchAll && !ArchFlags.empty()) {
+ // Look for a slice in the universal binary that matches each ArchFlag.
+ bool ArchFound;
+ for (unsigned i = 0; i < ArchFlags.size(); ++i) {
+ ArchFound = false;
for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
- E = UB->end_objects();
- I != E; ++I) {
- if (MachOObjectFile::getHostArch().getArchName() ==
- I->getArchFlagName()) {
- Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
- std::string ArchiveName;
- ArchiveName.clear();
+ E = UB->end_objects();
+ I != E; ++I) {
+ if (ArchFlags[i] == I->getArchFlagName()) {
+ ArchFound = true;
+ Expected<std::unique_ptr<ObjectFile>> ObjOrErr =
+ I->getAsObjectFile();
+ std::string ArchitectureName = "";
+ if (ArchFlags.size() > 1)
+ ArchitectureName = I->getArchFlagName();
if (ObjOrErr) {
ObjectFile &O = *ObjOrErr.get();
if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O))
- ProcessMachO(Filename, MachOOF);
+ ProcessMachO(Filename, MachOOF, "", ArchitectureName);
} else if (auto E = isNotObjectErrorInvalidFileType(
- ObjOrErr.takeError())) {
- report_error(Filename, std::move(E));
+ ObjOrErr.takeError())) {
+ report_error(Filename, StringRef(), std::move(E),
+ ArchitectureName);
continue;
} else if (Expected<std::unique_ptr<Archive>> AOrErr =
- I->getAsArchive()) {
+ I->getAsArchive()) {
std::unique_ptr<Archive> &A = *AOrErr;
- outs() << "Archive : " << Filename << "\n";
+ outs() << "Archive : " << Filename;
+ if (!ArchitectureName.empty())
+ outs() << " (architecture " << ArchitectureName << ")";
+ outs() << "\n";
if (ArchiveHeaders)
printArchiveHeaders(Filename, A.get(), !NonVerbose,
- ArchiveMemberOffsets);
+ ArchiveMemberOffsets, ArchitectureName);
Error Err = Error::success();
for (auto &C : A->children(Err)) {
Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
if (!ChildOrErr) {
if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
- report_error(Filename, C, std::move(E));
+ report_error(Filename, C, std::move(E), ArchitectureName);
continue;
}
if (MachOObjectFile *O =
dyn_cast<MachOObjectFile>(&*ChildOrErr.get()))
- ProcessMachO(Filename, O, O->getFileName());
+ ProcessMachO(Filename, O, O->getFileName(), ArchitectureName);
}
if (Err)
report_error(Filename, std::move(Err));
} else {
consumeError(AOrErr.takeError());
- error("Mach-O universal file: " + Filename + " for architecture " +
- StringRef(I->getArchFlagName()) +
+ error("Mach-O universal file: " + Filename + " for " +
+ "architecture " + StringRef(I->getArchFlagName()) +
" is not a Mach-O file or an archive file");
}
- return;
}
}
+ if (!ArchFound) {
+ WithColor::error(errs(), "llvm-objdump")
+ << "file: " + Filename + " does not contain "
+ << "architecture: " + ArchFlags[i] + "\n";
+ return;
+ }
}
- // Either all architectures have been specified or none have been specified
- // and this does not contain the host architecture so dump all the slices.
- bool moreThanOneArch = UB->getNumberOfObjects() > 1;
+ return;
+ }
+ // No architecture flags were specified so if this contains a slice that
+ // matches the host architecture dump only that.
+ if (!ArchAll) {
for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
- E = UB->end_objects();
- I != E; ++I) {
- Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
- std::string ArchitectureName = "";
- if (moreThanOneArch)
- ArchitectureName = I->getArchFlagName();
- if (ObjOrErr) {
- ObjectFile &Obj = *ObjOrErr.get();
- if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&Obj))
- ProcessMachO(Filename, MachOOF, "", ArchitectureName);
- } else if (auto E = isNotObjectErrorInvalidFileType(
- ObjOrErr.takeError())) {
- report_error(StringRef(), Filename, std::move(E), ArchitectureName);
- continue;
- } else if (Expected<std::unique_ptr<Archive>> AOrErr =
- I->getAsArchive()) {
- std::unique_ptr<Archive> &A = *AOrErr;
- outs() << "Archive : " << Filename;
- if (!ArchitectureName.empty())
- outs() << " (architecture " << ArchitectureName << ")";
- outs() << "\n";
- if (ArchiveHeaders)
- printArchiveHeaders(Filename, A.get(), !NonVerbose,
- ArchiveMemberOffsets, ArchitectureName);
- Error Err = Error::success();
- for (auto &C : A->children(Err)) {
- Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
- if (!ChildOrErr) {
- if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
- report_error(Filename, C, std::move(E), ArchitectureName);
- continue;
- }
- if (MachOObjectFile *O =
- dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) {
- if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(O))
- ProcessMachO(Filename, MachOOF, MachOOF->getFileName(),
- ArchitectureName);
+ E = UB->end_objects();
+ I != E; ++I) {
+ if (MachOObjectFile::getHostArch().getArchName() ==
+ I->getArchFlagName()) {
+ Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
+ std::string ArchiveName;
+ ArchiveName.clear();
+ if (ObjOrErr) {
+ ObjectFile &O = *ObjOrErr.get();
+ if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O))
+ ProcessMachO(Filename, MachOOF);
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ ObjOrErr.takeError())) {
+ report_error(Filename, std::move(E));
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
+ std::unique_ptr<Archive> &A = *AOrErr;
+ outs() << "Archive : " << Filename << "\n";
+ if (ArchiveHeaders)
+ printArchiveHeaders(Filename, A.get(), !NonVerbose,
+ ArchiveMemberOffsets);
+ Error Err = Error::success();
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ report_error(Filename, C, std::move(E));
+ continue;
+ }
+ if (MachOObjectFile *O =
+ dyn_cast<MachOObjectFile>(&*ChildOrErr.get()))
+ ProcessMachO(Filename, O, O->getFileName());
}
+ if (Err)
+ report_error(Filename, std::move(Err));
+ } else {
+ consumeError(AOrErr.takeError());
+ error("Mach-O universal file: " + Filename + " for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a Mach-O file or an archive file");
}
- if (Err)
- report_error(Filename, std::move(Err));
- } else {
- consumeError(AOrErr.takeError());
- error("Mach-O universal file: " + Filename + " for architecture " +
- StringRef(I->getArchFlagName()) +
- " is not a Mach-O file or an archive file");
+ return;
}
}
- return;
}
- if (ObjectFile *O = dyn_cast<ObjectFile>(&Bin)) {
- if (!checkMachOAndArchFlags(O, Filename))
- return;
- if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&*O)) {
- ProcessMachO(Filename, MachOOF);
- } else
- errs() << "llvm-objdump: '" << Filename << "': "
- << "Object is not a Mach-O file type.\n";
- return;
+ // Either all architectures have been specified or none have been specified
+ // and this does not contain the host architecture so dump all the slices.
+ bool moreThanOneArch = UB->getNumberOfObjects() > 1;
+ for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
+ E = UB->end_objects();
+ I != E; ++I) {
+ Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile();
+ std::string ArchitectureName = "";
+ if (moreThanOneArch)
+ ArchitectureName = I->getArchFlagName();
+ if (ObjOrErr) {
+ ObjectFile &Obj = *ObjOrErr.get();
+ if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&Obj))
+ ProcessMachO(Filename, MachOOF, "", ArchitectureName);
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ ObjOrErr.takeError())) {
+ report_error(StringRef(), Filename, std::move(E), ArchitectureName);
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
+ std::unique_ptr<Archive> &A = *AOrErr;
+ outs() << "Archive : " << Filename;
+ if (!ArchitectureName.empty())
+ outs() << " (architecture " << ArchitectureName << ")";
+ outs() << "\n";
+ if (ArchiveHeaders)
+ printArchiveHeaders(Filename, A.get(), !NonVerbose,
+ ArchiveMemberOffsets, ArchitectureName);
+ Error Err = Error::success();
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ report_error(Filename, C, std::move(E), ArchitectureName);
+ continue;
+ }
+ if (MachOObjectFile *O =
+ dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) {
+ if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(O))
+ ProcessMachO(Filename, MachOOF, MachOOF->getFileName(),
+ ArchitectureName);
+ }
+ }
+ if (Err)
+ report_error(Filename, std::move(Err));
+ } else {
+ consumeError(AOrErr.takeError());
+ error("Mach-O universal file: " + Filename + " for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a Mach-O file or an archive file");
+ }
}
- llvm_unreachable("Input object can't be invalid at this point");
}
// The block of info used by the Symbolizer call backs.
@@ -5609,7 +5636,9 @@ static void print_image_info64(SectionRef S, struct DisassembleInfo *info) {
else if(swift_version == 5)
outs() << " Swift 4.0";
else if(swift_version == 6)
- outs() << " Swift 4.1";
+ outs() << " Swift 4.1/Swift 4.2";
+ else if(swift_version == 7)
+ outs() << " Swift 5 or later";
else
outs() << " unknown future Swift version (" << swift_version << ")";
}
@@ -5660,7 +5689,9 @@ static void print_image_info32(SectionRef S, struct DisassembleInfo *info) {
else if(swift_version == 5)
outs() << " Swift 4.0";
else if(swift_version == 6)
- outs() << " Swift 4.1";
+ outs() << " Swift 4.1/Swift 4.2";
+ else if(swift_version == 7)
+ outs() << " Swift 5 or later";
else
outs() << " unknown future Swift version (" << swift_version << ")";
}
@@ -6172,8 +6203,9 @@ static void PrintXarFilesSummary(const char *XarFilename, xar_t xar) {
ScopedXarIter xi;
if (!xi) {
- errs() << "Can't obtain an xar iterator for xar archive "
- << XarFilename << "\n";
+ WithColor::error(errs(), "llvm-objdump")
+ << "can't obtain an xar iterator for xar archive " << XarFilename
+ << "\n";
return;
}
@@ -6181,8 +6213,9 @@ static void PrintXarFilesSummary(const char *XarFilename, xar_t xar) {
for (xf = xar_file_first(xar, xi); xf; xf = xar_file_next(xi)) {
ScopedXarIter xp;
if(!xp){
- errs() << "Can't obtain an xar iterator for xar archive "
- << XarFilename << "\n";
+ WithColor::error(errs(), "llvm-objdump")
+ << "can't obtain an xar iterator for xar archive " << XarFilename
+ << "\n";
return;
}
type = nullptr;
@@ -6306,7 +6339,7 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
std::error_code XarEC =
sys::fs::createTemporaryFile("llvm-objdump", "xar", FD, XarFilename);
if (XarEC) {
- errs() << XarEC.message() << "\n";
+ WithColor::error(errs(), "llvm-objdump") << XarEC.message() << "\n";
return;
}
ToolOutputFile XarFile(XarFilename, FD);
@@ -6319,7 +6352,8 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
ScopedXarFile xar(XarFilename.c_str(), READ);
if (!xar) {
- errs() << "Can't create temporary xar archive " << XarFilename << "\n";
+ WithColor::error(errs(), "llvm-objdump")
+ << "can't create temporary xar archive " << XarFilename << "\n";
return;
}
@@ -6327,7 +6361,7 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
std::error_code TocEC =
sys::fs::createTemporaryFile("llvm-objdump", "toc", TocFilename);
if (TocEC) {
- errs() << TocEC.message() << "\n";
+ WithColor::error(errs(), "llvm-objdump") << TocEC.message() << "\n";
return;
}
xar_serialize(xar, TocFilename.c_str());
@@ -6344,7 +6378,7 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
MemoryBuffer::getFileOrSTDIN(TocFilename.c_str());
if (std::error_code EC = FileOrErr.getError()) {
- errs() << EC.message() << "\n";
+ WithColor::error(errs(), "llvm-objdump") << EC.message() << "\n";
return;
}
std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get();
@@ -6359,8 +6393,9 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
// TODO: Go through the xar's files.
ScopedXarIter xi;
if(!xi){
- errs() << "Can't obtain an xar iterator for xar archive "
- << XarFilename.c_str() << "\n";
+ WithColor::error(errs(), "llvm-objdump")
+ << "can't obtain an xar iterator for xar archive "
+ << XarFilename.c_str() << "\n";
return;
}
for(xar_file_t xf = xar_file_first(xar, xi); xf; xf = xar_file_next(xi)){
@@ -6370,8 +6405,9 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
ScopedXarIter xp;
if(!xp){
- errs() << "Can't obtain an xar iterator for xar archive "
- << XarFilename.c_str() << "\n";
+ WithColor::error(errs(), "llvm-objdump")
+ << "can't obtain an xar iterator for xar archive "
+ << XarFilename.c_str() << "\n";
return;
}
member_name = NULL;
@@ -6805,7 +6841,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
// Package up features to be passed to target/subtarget
std::string FeaturesStr;
- if (MAttrs.size()) {
+ if (!MAttrs.empty()) {
SubtargetFeatures Features;
for (unsigned i = 0; i != MAttrs.size(); ++i)
Features.AddFeature(MAttrs[i]);
@@ -6848,8 +6884,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
// IP->setCommentStream(CommentStream);
if (!AsmInfo || !STI || !DisAsm || !IP) {
- errs() << "error: couldn't initialize disassembler for target "
- << TripleName << '\n';
+ WithColor::error(errs(), "llvm-objdump")
+ << "couldn't initialize disassembler for target " << TripleName << '\n';
return;
}
@@ -6890,8 +6926,9 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
}
if (ThumbTarget && (!ThumbAsmInfo || !ThumbSTI || !ThumbDisAsm || !ThumbIP)) {
- errs() << "error: couldn't initialize disassembler for target "
- << ThumbTripleName << '\n';
+ WithColor::error(errs(), "llvm-objdump")
+ << "couldn't initialize disassembler for target " << ThumbTripleName
+ << '\n';
return;
}
@@ -6910,7 +6947,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
BaseSegmentAddress);
// Sort the symbols by address, just in case they didn't come in that way.
- llvm::sort(Symbols.begin(), Symbols.end(), SymbolSorter());
+ llvm::sort(Symbols, SymbolSorter());
// Build a data in code table that is sorted on by the address of each entry.
uint64_t BaseAddress = 0;
@@ -6935,6 +6972,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
std::unique_ptr<DIContext> diContext;
ObjectFile *DbgObj = MachOOF;
+ std::unique_ptr<MemoryBuffer> DSYMBuf;
// Try to find debug info and set up the DIContext for it.
if (UseDbg) {
// A separate DSym file path was specified, parse it as a macho file,
@@ -6943,22 +6981,28 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
MemoryBuffer::getFileOrSTDIN(DSYMFile);
if (std::error_code EC = BufOrErr.getError()) {
- errs() << "llvm-objdump: " << Filename << ": " << EC.message() << '\n';
+ report_error(DSYMFile, errorCodeToError(EC));
return;
}
+
Expected<std::unique_ptr<MachOObjectFile>> DbgObjCheck =
ObjectFile::createMachOObjectFile(BufOrErr.get()->getMemBufferRef());
- if (DbgObjCheck.takeError())
- report_error(MachOOF->getFileName(), DbgObjCheck.takeError());
+ if (Error E = DbgObjCheck.takeError()) {
+ report_error(DSYMFile, std::move(E));
+ return;
+ }
+
DbgObj = DbgObjCheck.get().release();
+ // We need to keep the file alive, because we're replacing DbgObj with it.
+ DSYMBuf = std::move(BufOrErr.get());
}
// Setup the DIContext
diContext = DWARFContext::create(*DbgObj);
}
- if (FilterSections.size() == 0)
+ if (FilterSections.empty())
outs() << "(" << DisSegName << "," << DisSectName << ") section\n";
for (unsigned SectIdx = 0; SectIdx != Sections.size(); SectIdx++) {
@@ -7021,7 +7065,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
unsigned int Arch = MachOOF->getArch();
// Skip all symbols if this is a stubs file.
- if (Bytes.size() == 0)
+ if (Bytes.empty())
return;
// If the section has symbols but no symbol at the start of the section
@@ -7228,7 +7272,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
outs() << format("\t.short\t0x%04x\n", opcode);
Size = 2;
} else{
- errs() << "llvm-objdump: warning: invalid instruction encoding\n";
+ WithColor::warning(errs(), "llvm-objdump")
+ << "invalid instruction encoding\n";
if (Size == 0)
Size = 1; // skip illegible bytes
}
@@ -7275,7 +7320,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
*(Bytes.data() + Index) & 0xff);
InstSize = 1; // skip exactly one illegible byte and move on.
} else {
- errs() << "llvm-objdump: warning: invalid instruction encoding\n";
+ WithColor::warning(errs(), "llvm-objdump")
+ << "invalid instruction encoding\n";
if (InstSize == 0)
InstSize = 1; // skip illegible bytes
}
diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp
index 8041e6f59940..ba8d3c5b8d5c 100644
--- a/tools/llvm-objdump/llvm-objdump.cpp
+++ b/tools/llvm-objdump/llvm-objdump.cpp
@@ -42,6 +42,7 @@
#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/MachO.h"
+#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/Wasm.h"
#include "llvm/Support/Casting.h"
@@ -55,8 +56,10 @@
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/StringSaver.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cctype>
@@ -91,12 +94,11 @@ static cl::alias
DisassembleAlld("D", cl::desc("Alias for --disassemble-all"),
cl::aliasopt(DisassembleAll));
-cl::opt<std::string> llvm::Demangle("demangle",
- cl::desc("Demangle symbols names"),
- cl::ValueOptional, cl::init("none"));
+cl::opt<bool> llvm::Demangle("demangle", cl::desc("Demangle symbols names"),
+ cl::init(false));
static cl::alias DemangleShort("C", cl::desc("Alias for --demangle"),
- cl::aliasopt(Demangle));
+ cl::aliasopt(llvm::Demangle));
static cl::list<std::string>
DisassembleFunctions("df",
@@ -105,7 +107,11 @@ DisassembleFunctions("df",
static StringSet<> DisasmFuncsSet;
cl::opt<bool>
-llvm::Relocations("r", cl::desc("Display the relocation entries in the file"));
+llvm::Relocations("reloc",
+ cl::desc("Display the relocation entries in the file"));
+static cl::alias RelocationsShort("r", cl::desc("Alias for --reloc"),
+ cl::NotHidden,
+ cl::aliasopt(llvm::Relocations));
cl::opt<bool>
llvm::DynamicRelocations("dynamic-reloc",
@@ -115,10 +121,16 @@ DynamicRelocationsd("R", cl::desc("Alias for --dynamic-reloc"),
cl::aliasopt(DynamicRelocations));
cl::opt<bool>
-llvm::SectionContents("s", cl::desc("Display the content of each section"));
+ llvm::SectionContents("full-contents",
+ cl::desc("Display the content of each section"));
+static cl::alias SectionContentsShort("s",
+ cl::desc("Alias for --full-contents"),
+ cl::aliasopt(SectionContents));
-cl::opt<bool>
-llvm::SymbolTable("t", cl::desc("Display the symbol table"));
+cl::opt<bool> llvm::SymbolTable("syms", cl::desc("Display the symbol table"));
+static cl::alias SymbolTableShort("t", cl::desc("Alias for --syms"),
+ cl::NotHidden,
+ cl::aliasopt(llvm::SymbolTable));
cl::opt<bool>
llvm::ExportsTrie("exports-trie", cl::desc("Display mach-o exported symbols"));
@@ -253,8 +265,17 @@ cl::opt<unsigned long long>
StartAddress("start-address", cl::desc("Disassemble beginning at address"),
cl::value_desc("address"), cl::init(0));
cl::opt<unsigned long long>
- StopAddress("stop-address", cl::desc("Stop disassembly at address"),
+ StopAddress("stop-address",
+ cl::desc("Stop disassembly at address"),
cl::value_desc("address"), cl::init(UINT64_MAX));
+
+cl::opt<bool> DisassembleZeroes(
+ "disassemble-zeroes",
+ cl::desc("Do not skip blocks of zeroes when disassembling"));
+cl::alias DisassembleZeroesShort("z",
+ cl::desc("Alias for --disassemble-zeroes"),
+ cl::aliasopt(DisassembleZeroes));
+
static StringRef ToolName;
typedef std::vector<std::tuple<uint64_t, StringRef, uint8_t>> SectionSymbolsTy;
@@ -326,33 +347,35 @@ SectionFilter ToolSectionFilter(llvm::object::ObjectFile const &O) {
void llvm::error(std::error_code EC) {
if (!EC)
return;
-
- errs() << ToolName << ": error reading file: " << EC.message() << ".\n";
+ WithColor::error(errs(), ToolName)
+ << "reading file: " << EC.message() << ".\n";
errs().flush();
exit(1);
}
LLVM_ATTRIBUTE_NORETURN void llvm::error(Twine Message) {
- errs() << ToolName << ": " << Message << ".\n";
+ WithColor::error(errs(), ToolName) << Message << ".\n";
errs().flush();
exit(1);
}
void llvm::warn(StringRef Message) {
- errs() << ToolName << ": warning: " << Message << ".\n";
+ WithColor::warning(errs(), ToolName) << Message << ".\n";
errs().flush();
}
LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File,
Twine Message) {
- errs() << ToolName << ": '" << File << "': " << Message << ".\n";
+ WithColor::error(errs(), ToolName)
+ << "'" << File << "': " << Message << ".\n";
exit(1);
}
LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File,
std::error_code EC) {
assert(EC);
- errs() << ToolName << ": '" << File << "': " << EC.message() << ".\n";
+ WithColor::error(errs(), ToolName)
+ << "'" << File << "': " << EC.message() << ".\n";
exit(1);
}
@@ -361,9 +384,9 @@ LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File,
assert(E);
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(std::move(E), OS, "");
+ logAllUnhandledErrors(std::move(E), OS);
OS.flush();
- errs() << ToolName << ": '" << File << "': " << Buf;
+ WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf;
exit(1);
}
@@ -372,7 +395,7 @@ LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef ArchiveName,
llvm::Error E,
StringRef ArchitectureName) {
assert(E);
- errs() << ToolName << ": ";
+ WithColor::error(errs(), ToolName);
if (ArchiveName != "")
errs() << ArchiveName << "(" << FileName << ")";
else
@@ -381,7 +404,7 @@ LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef ArchiveName,
errs() << " (for architecture " << ArchitectureName << ")";
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(std::move(E), OS, "");
+ logAllUnhandledErrors(std::move(E), OS);
OS.flush();
errs() << ": " << Buf;
exit(1);
@@ -407,18 +430,16 @@ static const Target *getTarget(const ObjectFile *Obj = nullptr) {
// Figure out the target triple.
llvm::Triple TheTriple("unknown-unknown-unknown");
if (TripleName.empty()) {
- if (Obj) {
+ if (Obj)
TheTriple = Obj->makeTriple();
- }
} else {
TheTriple.setTriple(Triple::normalize(TripleName));
// Use the triple, but also try to combine with ARM build attributes.
if (Obj) {
auto Arch = Obj->getArch();
- if (Arch == Triple::arm || Arch == Triple::armeb) {
+ if (Arch == Triple::arm || Arch == Triple::armeb)
Obj->setARMSubArch(TheTriple);
- }
}
}
@@ -438,22 +459,35 @@ static const Target *getTarget(const ObjectFile *Obj = nullptr) {
return TheTarget;
}
-bool llvm::RelocAddressLess(RelocationRef a, RelocationRef b) {
- return a.getOffset() < b.getOffset();
+bool llvm::isRelocAddressLess(RelocationRef A, RelocationRef B) {
+ return A.getOffset() < B.getOffset();
+}
+
+static std::string demangle(StringRef Name) {
+ char *Demangled = nullptr;
+ if (Name.startswith("_Z"))
+ Demangled = itaniumDemangle(Name.data(), Demangled, nullptr, nullptr);
+ else if (Name.startswith("?"))
+ Demangled = microsoftDemangle(Name.data(), Demangled, nullptr, nullptr);
+
+ if (!Demangled)
+ return Name;
+
+ std::string Ret = Demangled;
+ free(Demangled);
+ return Ret;
}
template <class ELFT>
static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
const RelocationRef &RelRef,
SmallVectorImpl<char> &Result) {
- DataRefImpl Rel = RelRef.getRawDataRefImpl();
-
typedef typename ELFObjectFile<ELFT>::Elf_Sym Elf_Sym;
typedef typename ELFObjectFile<ELFT>::Elf_Shdr Elf_Shdr;
typedef typename ELFObjectFile<ELFT>::Elf_Rela Elf_Rela;
const ELFFile<ELFT> &EF = *Obj->getELFFile();
-
+ DataRefImpl Rel = RelRef.getRawDataRefImpl();
auto SecOrErr = EF.getSection(Rel.d.a);
if (!SecOrErr)
return errorToErrorCode(SecOrErr.takeError());
@@ -471,11 +505,11 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
if (!StrTabOrErr)
return errorToErrorCode(StrTabOrErr.takeError());
StringRef StrTab = *StrTabOrErr;
- int64_t addend = 0;
+ int64_t Addend = 0;
// If there is no Symbol associated with the relocation, we set the undef
// boolean value to 'true'. This will prevent us from calling functions that
// requires the relocation to be associated with a symbol.
- bool undef = false;
+ bool Undef = false;
switch (Sec->sh_type) {
default:
return object_error::parse_failed;
@@ -485,13 +519,13 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
}
case ELF::SHT_RELA: {
const Elf_Rela *ERela = Obj->getRela(Rel);
- addend = ERela->r_addend;
- undef = ERela->getSymbol(false) == 0;
+ Addend = ERela->r_addend;
+ Undef = ERela->getSymbol(false) == 0;
break;
}
}
- StringRef Target;
- if (!undef) {
+ std::string Target;
+ if (!Undef) {
symbol_iterator SI = RelRef.getSymbol();
const Elf_Sym *symb = Obj->getSymbol(SI->getRawDataRefImpl());
if (symb->getType() == ELF::STT_SECTION) {
@@ -507,20 +541,23 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
Expected<StringRef> SymName = symb->getName(StrTab);
if (!SymName)
return errorToErrorCode(SymName.takeError());
- Target = *SymName;
+ if (Demangle)
+ Target = demangle(*SymName);
+ else
+ Target = *SymName;
}
} else
Target = "*ABS*";
// Default scheme is to print Target, as well as "+ <addend>" for nonzero
// addend. Should be acceptable for all normal purposes.
- std::string fmtbuf;
- raw_string_ostream fmt(fmtbuf);
- fmt << Target;
- if (addend != 0)
- fmt << (addend < 0 ? "" : "+") << addend;
- fmt.flush();
- Result.append(fmtbuf.begin(), fmtbuf.end());
+ std::string FmtBuf;
+ raw_string_ostream Fmt(FmtBuf);
+ Fmt << Target;
+ if (Addend != 0)
+ Fmt << (Addend < 0 ? "" : "+") << Addend;
+ Fmt.flush();
+ Result.append(FmtBuf.begin(), FmtBuf.end());
return std::error_code();
}
@@ -551,18 +588,15 @@ static std::error_code getRelocationValueString(const COFFObjectFile *Obj,
static void printRelocationTargetName(const MachOObjectFile *O,
const MachO::any_relocation_info &RE,
- raw_string_ostream &fmt) {
- bool IsScattered = O->isRelocationScattered(RE);
-
+ raw_string_ostream &Fmt) {
// Target of a scattered relocation is an address. In the interest of
// generating pretty output, scan through the symbol table looking for a
// symbol that aligns with that address. If we find one, print it.
// Otherwise, we just print the hex address of the target.
- if (IsScattered) {
+ if (O->isRelocationScattered(RE)) {
uint32_t Val = O->getPlainRelocationSymbolNum(RE);
for (const SymbolRef &Symbol : O->symbols()) {
- std::error_code ec;
Expected<uint64_t> Addr = Symbol.getAddress();
if (!Addr)
report_error(O->getFileName(), Addr.takeError());
@@ -571,7 +605,7 @@ static void printRelocationTargetName(const MachOObjectFile *O,
Expected<StringRef> Name = Symbol.getName();
if (!Name)
report_error(O->getFileName(), Name.takeError());
- fmt << *Name;
+ Fmt << *Name;
return;
}
@@ -586,11 +620,11 @@ static void printRelocationTargetName(const MachOObjectFile *O,
continue;
if ((ec = Section.getName(Name)))
report_error(O->getFileName(), ec);
- fmt << Name;
+ Fmt << Name;
return;
}
- fmt << format("0x%x", Val);
+ Fmt << format("0x%x", Val);
return;
}
@@ -599,9 +633,11 @@ static void printRelocationTargetName(const MachOObjectFile *O,
uint64_t Val = O->getPlainRelocationSymbolNum(RE);
if (O->getAnyRelocationType(RE) == MachO::ARM64_RELOC_ADDEND) {
- fmt << format("0x%0" PRIx64, Val);
+ Fmt << format("0x%0" PRIx64, Val);
return;
- } else if (isExtern) {
+ }
+
+ if (isExtern) {
symbol_iterator SI = O->symbol_begin();
advance(SI, Val);
Expected<StringRef> SOrErr = SI->getName();
@@ -612,21 +648,21 @@ static void printRelocationTargetName(const MachOObjectFile *O,
section_iterator SI = O->section_begin();
// Adjust for the fact that sections are 1-indexed.
if (Val == 0) {
- fmt << "0 (?,?)";
+ Fmt << "0 (?,?)";
return;
}
- uint32_t i = Val - 1;
- while (i != 0 && SI != O->section_end()) {
- i--;
+ uint32_t I = Val - 1;
+ while (I != 0 && SI != O->section_end()) {
+ --I;
advance(SI, 1);
}
if (SI == O->section_end())
- fmt << Val << " (?,?)";
+ Fmt << Val << " (?,?)";
else
SI->getName(S);
}
- fmt << S;
+ Fmt << S;
}
static std::error_code getRelocationValueString(const WasmObjectFile *Obj,
@@ -634,12 +670,12 @@ static std::error_code getRelocationValueString(const WasmObjectFile *Obj,
SmallVectorImpl<char> &Result) {
const wasm::WasmRelocation& Rel = Obj->getWasmRelocation(RelRef);
symbol_iterator SI = RelRef.getSymbol();
- std::string fmtbuf;
- raw_string_ostream fmt(fmtbuf);
+ std::string FmtBuf;
+ raw_string_ostream Fmt(FmtBuf);
if (SI == Obj->symbol_end()) {
// Not all wasm relocations have symbols associated with them.
// In particular R_WEBASSEMBLY_TYPE_INDEX_LEB.
- fmt << Rel.Index;
+ Fmt << Rel.Index;
} else {
Expected<StringRef> SymNameOrErr = SI->getName();
if (!SymNameOrErr)
@@ -647,9 +683,9 @@ static std::error_code getRelocationValueString(const WasmObjectFile *Obj,
StringRef SymName = *SymNameOrErr;
Result.append(SymName.begin(), SymName.end());
}
- fmt << (Rel.Addend < 0 ? "" : "+") << Rel.Addend;
- fmt.flush();
- Result.append(fmtbuf.begin(), fmtbuf.end());
+ Fmt << (Rel.Addend < 0 ? "" : "+") << Rel.Addend;
+ Fmt.flush();
+ Result.append(FmtBuf.begin(), FmtBuf.end());
return std::error_code();
}
@@ -661,8 +697,8 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj,
unsigned Arch = Obj->getArch();
- std::string fmtbuf;
- raw_string_ostream fmt(fmtbuf);
+ std::string FmtBuf;
+ raw_string_ostream Fmt(FmtBuf);
unsigned Type = Obj->getAnyRelocationType(RE);
bool IsPCRel = Obj->getAnyRelocationPCRel(RE);
@@ -671,15 +707,13 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj,
// X86_64 has entirely custom relocation types.
if (Arch == Triple::x86_64) {
- bool isPCRel = Obj->getAnyRelocationPCRel(RE);
-
switch (Type) {
case MachO::X86_64_RELOC_GOT_LOAD:
case MachO::X86_64_RELOC_GOT: {
- printRelocationTargetName(Obj, RE, fmt);
- fmt << "@GOT";
- if (isPCRel)
- fmt << "PCREL";
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "@GOT";
+ if (IsPCRel)
+ Fmt << "PCREL";
break;
}
case MachO::X86_64_RELOC_SUBTRACTOR: {
@@ -697,31 +731,31 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj,
// The X86_64_RELOC_UNSIGNED contains the minuend symbol;
// X86_64_RELOC_SUBTRACTOR contains the subtrahend.
- printRelocationTargetName(Obj, RENext, fmt);
- fmt << "-";
- printRelocationTargetName(Obj, RE, fmt);
+ printRelocationTargetName(Obj, RENext, Fmt);
+ Fmt << "-";
+ printRelocationTargetName(Obj, RE, Fmt);
break;
}
case MachO::X86_64_RELOC_TLV:
- printRelocationTargetName(Obj, RE, fmt);
- fmt << "@TLV";
- if (isPCRel)
- fmt << "P";
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "@TLV";
+ if (IsPCRel)
+ Fmt << "P";
break;
case MachO::X86_64_RELOC_SIGNED_1:
- printRelocationTargetName(Obj, RE, fmt);
- fmt << "-1";
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "-1";
break;
case MachO::X86_64_RELOC_SIGNED_2:
- printRelocationTargetName(Obj, RE, fmt);
- fmt << "-2";
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "-2";
break;
case MachO::X86_64_RELOC_SIGNED_4:
- printRelocationTargetName(Obj, RE, fmt);
- fmt << "-4";
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "-4";
break;
default:
- printRelocationTargetName(Obj, RE, fmt);
+ printRelocationTargetName(Obj, RE, Fmt);
break;
}
// X86 and ARM share some relocation types in common.
@@ -744,9 +778,9 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj,
report_error(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after "
"GENERIC_RELOC_SECTDIFF.");
- printRelocationTargetName(Obj, RE, fmt);
- fmt << "-";
- printRelocationTargetName(Obj, RENext, fmt);
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "-";
+ printRelocationTargetName(Obj, RENext, Fmt);
break;
}
}
@@ -765,20 +799,20 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj,
report_error(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after "
"GENERIC_RELOC_LOCAL_SECTDIFF.");
- printRelocationTargetName(Obj, RE, fmt);
- fmt << "-";
- printRelocationTargetName(Obj, RENext, fmt);
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "-";
+ printRelocationTargetName(Obj, RENext, Fmt);
break;
}
case MachO::GENERIC_RELOC_TLV: {
- printRelocationTargetName(Obj, RE, fmt);
- fmt << "@TLV";
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "@TLV";
if (IsPCRel)
- fmt << "P";
+ Fmt << "P";
break;
}
default:
- printRelocationTargetName(Obj, RE, fmt);
+ printRelocationTargetName(Obj, RE, Fmt);
}
} else { // ARM-specific relocations
switch (Type) {
@@ -789,10 +823,10 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj,
bool isUpper = (Obj->getAnyRelocationLength(RE) & 0x1) == 1;
if (isUpper)
- fmt << ":upper16:(";
+ Fmt << ":upper16:(";
else
- fmt << ":lower16:(";
- printRelocationTargetName(Obj, RE, fmt);
+ Fmt << ":lower16:(";
+ printRelocationTargetName(Obj, RE, Fmt);
DataRefImpl RelNext = Rel;
Obj->moveRelocationNext(RelNext);
@@ -813,21 +847,21 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj,
// ARM_RELOC_HALF_SECTDIFF encodes the second section in the
// symbol/section pointer of the follow-on relocation.
if (Type == MachO::ARM_RELOC_HALF_SECTDIFF) {
- fmt << "-";
- printRelocationTargetName(Obj, RENext, fmt);
+ Fmt << "-";
+ printRelocationTargetName(Obj, RENext, Fmt);
}
- fmt << ")";
+ Fmt << ")";
break;
}
- default: { printRelocationTargetName(Obj, RE, fmt); }
+ default: { printRelocationTargetName(Obj, RE, Fmt); }
}
}
} else
- printRelocationTargetName(Obj, RE, fmt);
+ printRelocationTargetName(Obj, RE, Fmt);
- fmt.flush();
- Result.append(fmtbuf.begin(), fmtbuf.end());
+ Fmt.flush();
+ Result.append(FmtBuf.begin(), FmtBuf.end());
return std::error_code();
}
@@ -849,8 +883,7 @@ static std::error_code getRelocationValueString(const RelocationRef &Rel,
/// relocations, usually because it is the trailing part of a multipart
/// relocation that will be printed as part of the leading relocation.
static bool getHidden(RelocationRef RelRef) {
- const ObjectFile *Obj = RelRef.getObject();
- auto *MachO = dyn_cast<MachOObjectFile>(Obj);
+ auto *MachO = dyn_cast<MachOObjectFile>(RelRef.getObject());
if (!MachO)
return false;
@@ -860,10 +893,10 @@ static bool getHidden(RelocationRef RelRef) {
// On arches that use the generic relocations, GENERIC_RELOC_PAIR
// is always hidden.
- if (Arch == Triple::x86 || Arch == Triple::arm || Arch == Triple::ppc) {
- if (Type == MachO::GENERIC_RELOC_PAIR)
- return true;
- } else if (Arch == Triple::x86_64) {
+ if (Arch == Triple::x86 || Arch == Triple::arm || Arch == Triple::ppc)
+ return Type == MachO::GENERIC_RELOC_PAIR;
+
+ if (Arch == Triple::x86_64) {
// On x86_64, X86_64_RELOC_UNSIGNED is hidden only when it follows
// an X86_64_RELOC_SUBTRACTOR.
if (Type == MachO::X86_64_RELOC_UNSIGNED && Rel.d.a > 0) {
@@ -1038,27 +1071,27 @@ public:
auto Preamble = " { ";
auto Separator = "";
StringRef Fmt = "\t\t\t%08" PRIx64 ": ";
- std::vector<RelocationRef>::const_iterator rel_cur = Rels->begin();
- std::vector<RelocationRef>::const_iterator rel_end = Rels->end();
+ std::vector<RelocationRef>::const_iterator RelCur = Rels->begin();
+ std::vector<RelocationRef>::const_iterator RelEnd = Rels->end();
// Hexagon's packets require relocations to be inline rather than
// clustered at the end of the packet.
auto PrintReloc = [&]() -> void {
- while ((rel_cur != rel_end) && (rel_cur->getOffset() <= Address)) {
- if (rel_cur->getOffset() == Address) {
- SmallString<16> name;
- SmallString<32> val;
- rel_cur->getTypeName(name);
- error(getRelocationValueString(*rel_cur, val));
- OS << Separator << format(Fmt.data(), Address) << name << "\t" << val
+ while ((RelCur != RelEnd) && (RelCur->getOffset() <= Address)) {
+ if (RelCur->getOffset() == Address) {
+ SmallString<16> Name;
+ SmallString<32> Val;
+ RelCur->getTypeName(Name);
+ error(getRelocationValueString(*RelCur, Val));
+ OS << Separator << format(Fmt.data(), Address) << Name << "\t" << Val
<< "\n";
return;
}
- rel_cur++;
+ ++RelCur;
}
};
- while(!HeadTail.first.empty()) {
+ while (!HeadTail.first.empty()) {
OS << Separator;
Separator = "\n";
if (SP && (PrintSource || PrintLines))
@@ -1068,7 +1101,7 @@ public:
Preamble = " ";
StringRef Inst;
auto Duplex = HeadTail.first.split('\v');
- if(!Duplex.second.empty()){
+ if (!Duplex.second.empty()) {
OS << Duplex.first;
OS << "; ";
Inst = Duplex.second;
@@ -1200,7 +1233,6 @@ addDynamicElfSymbols(const ELFObjectFile<ELFT> *Obj,
Expected<uint64_t> AddressOrErr = Symbol.getAddress();
if (!AddressOrErr)
report_error(Obj->getFileName(), AddressOrErr.takeError());
- uint64_t Address = *AddressOrErr;
Expected<StringRef> Name = Symbol.getName();
if (!Name)
@@ -1215,7 +1247,7 @@ addDynamicElfSymbols(const ELFObjectFile<ELFT> *Obj,
if (SecI == Obj->section_end())
continue;
- AllSymbols[*SecI].emplace_back(Address, *Name, SymbolType);
+ AllSymbols[*SecI].emplace_back(*AddressOrErr, *Name, SymbolType);
}
}
@@ -1235,7 +1267,60 @@ addDynamicElfSymbols(const ObjectFile *Obj,
llvm_unreachable("Unsupported binary format");
}
-static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
+static void addPltEntries(const ObjectFile *Obj,
+ std::map<SectionRef, SectionSymbolsTy> &AllSymbols,
+ StringSaver &Saver) {
+ Optional<SectionRef> Plt = None;
+ for (const SectionRef &Section : Obj->sections()) {
+ StringRef Name;
+ if (Section.getName(Name))
+ continue;
+ if (Name == ".plt")
+ Plt = Section;
+ }
+ if (!Plt)
+ return;
+ if (auto *ElfObj = dyn_cast<ELFObjectFileBase>(Obj)) {
+ for (auto PltEntry : ElfObj->getPltAddresses()) {
+ SymbolRef Symbol(PltEntry.first, ElfObj);
+ uint8_t SymbolType = getElfSymbolType(Obj, Symbol);
+
+ Expected<StringRef> NameOrErr = Symbol.getName();
+ if (!NameOrErr)
+ report_error(Obj->getFileName(), NameOrErr.takeError());
+ if (NameOrErr->empty())
+ continue;
+ StringRef Name = Saver.save((*NameOrErr + "@plt").str());
+
+ AllSymbols[*Plt].emplace_back(PltEntry.second, Name, SymbolType);
+ }
+ }
+}
+
+// Normally the disassembly output will skip blocks of zeroes. This function
+// returns the number of zero bytes that can be skipped when dumping the
+// disassembly of the instructions in Buf.
+static size_t countSkippableZeroBytes(ArrayRef<uint8_t> Buf) {
+ // When -z or --disassemble-zeroes are given we always dissasemble them.
+ if (DisassembleZeroes)
+ return 0;
+
+ // Find the number of leading zeroes.
+ size_t N = 0;
+ while (N < Buf.size() && !Buf[N])
+ ++N;
+
+ // We may want to skip blocks of zero bytes, but unless we see
+ // at least 8 of them in a row.
+ if (N < 8)
+ return 0;
+
+ // We skip zeroes in multiples of 4 because do not want to truncate an
+ // instruction if it starts with a zero byte.
+ return N & ~0x3;
+}
+
+static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
if (StartAddress > StopAddress)
error("Start address should be less than stop address");
@@ -1243,10 +1328,9 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
// Package up features to be passed to target/subtarget
SubtargetFeatures Features = Obj->getFeatures();
- if (MAttrs.size()) {
- for (unsigned i = 0; i != MAttrs.size(); ++i)
- Features.AddFeature(MAttrs[i]);
- }
+ if (!MAttrs.empty())
+ for (unsigned I = 0; I != MAttrs.size(); ++I)
+ Features.AddFeature(MAttrs[I]);
std::unique_ptr<const MCRegisterInfo> MRI(
TheTarget->createMCRegInfo(TripleName));
@@ -1342,6 +1426,10 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
if (AllSymbols.empty() && Obj->isELF())
addDynamicElfSymbols(Obj, AllSymbols);
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ addPltEntries(Obj, AllSymbols, Saver);
+
// Create a mapping from virtual address to section.
std::vector<std::pair<uint64_t, SectionRef>> SectionAddresses;
for (SectionRef Sec : Obj->sections())
@@ -1411,8 +1499,8 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
}
}
- llvm::sort(DataMappingSymsAddr.begin(), DataMappingSymsAddr.end());
- llvm::sort(TextMappingSymsAddr.begin(), TextMappingSymsAddr.end());
+ llvm::sort(DataMappingSymsAddr);
+ llvm::sort(TextMappingSymsAddr);
if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) {
// AMDGPU disassembler uses symbolizer for printing labels
@@ -1437,7 +1525,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
}
// Sort relocations by address.
- llvm::sort(Rels.begin(), Rels.end(), RelocAddressLess);
+ llvm::sort(Rels, isRelocAddressLess);
StringRef SegmentName = "";
if (const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(Obj)) {
@@ -1467,15 +1555,16 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
uint64_t Index;
bool PrintedSection = false;
- std::vector<RelocationRef>::const_iterator rel_cur = Rels.begin();
- std::vector<RelocationRef>::const_iterator rel_end = Rels.end();
+ std::vector<RelocationRef>::const_iterator RelCur = Rels.begin();
+ std::vector<RelocationRef>::const_iterator RelEnd = Rels.end();
// Disassemble symbol by symbol.
- for (unsigned si = 0, se = Symbols.size(); si != se; ++si) {
- uint64_t Start = std::get<0>(Symbols[si]) - SectionAddr;
+ for (unsigned SI = 0, SE = Symbols.size(); SI != SE; ++SI) {
+ uint64_t Start = std::get<0>(Symbols[SI]) - SectionAddr;
// The end is either the section end or the beginning of the next
// symbol.
- uint64_t End =
- (si == se - 1) ? SectSize : std::get<0>(Symbols[si + 1]) - SectionAddr;
+ uint64_t End = (SI == SE - 1)
+ ? SectSize
+ : std::get<0>(Symbols[SI + 1]) - SectionAddr;
// Don't try to disassemble beyond the end of section contents.
if (End > SectSize)
End = SectSize;
@@ -1492,7 +1581,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
/// Skip if user requested specific symbols and this is not in the list
if (!DisasmFuncsSet.empty() &&
- !DisasmFuncsSet.count(std::get<1>(Symbols[si])))
+ !DisasmFuncsSet.count(std::get<1>(Symbols[SI])))
continue;
if (!PrintedSection) {
@@ -1508,12 +1597,12 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
End = StopAddress - SectionAddr;
if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) {
- if (std::get<2>(Symbols[si]) == ELF::STT_AMDGPU_HSA_KERNEL) {
+ if (std::get<2>(Symbols[SI]) == ELF::STT_AMDGPU_HSA_KERNEL) {
// skip amd_kernel_code_t at the begining of kernel symbol (256 bytes)
Start += 256;
}
- if (si == se - 1 ||
- std::get<2>(Symbols[si + 1]) == ELF::STT_AMDGPU_HSA_KERNEL) {
+ if (SI == SE - 1 ||
+ std::get<2>(Symbols[SI + 1]) == ELF::STT_AMDGPU_HSA_KERNEL) {
// cut trailing zeroes at the end of kernel
// cut up to 256 bytes
const uint64_t EndAlign = 256;
@@ -1524,25 +1613,15 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
}
}
- auto PrintSymbol = [](StringRef Name) {
- outs() << '\n' << Name << ":\n";
- };
- StringRef SymbolName = std::get<1>(Symbols[si]);
- if (Demangle.getValue() == "" || Demangle.getValue() == "itanium") {
- char *DemangledSymbol = nullptr;
- size_t Size = 0;
- int Status;
- DemangledSymbol =
- itaniumDemangle(SymbolName.data(), DemangledSymbol, &Size, &Status);
- if (Status == 0)
- PrintSymbol(StringRef(DemangledSymbol));
- else
- PrintSymbol(SymbolName);
+ outs() << '\n';
+ if (!NoLeadingAddr)
+ outs() << format("%016" PRIx64 " ", SectionAddr + Start);
- if (Size != 0)
- free(DemangledSymbol);
- } else
- PrintSymbol(SymbolName);
+ StringRef SymbolName = std::get<1>(Symbols[SI]);
+ if (Demangle)
+ outs() << demangle(SymbolName) << ":\n";
+ else
+ outs() << SymbolName << ":\n";
// Don't print raw contents of a virtual section. A virtual section
// doesn't have any contents in the file.
@@ -1570,7 +1649,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
// same section. We rely on the markers introduced to
// understand what we need to dump. If the data marker is within a
// function, it is denoted as a word/short etc
- if (isArmElf(Obj) && std::get<2>(Symbols[si]) != ELF::STT_OBJECT &&
+ if (isArmElf(Obj) && std::get<2>(Symbols[SI]) != ELF::STT_OBJECT &&
!DisassembleAll) {
uint64_t Stride = 0;
@@ -1634,7 +1713,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
// disassembling text (applicable all architectures),
// we are in a situation where we must print the data and not
// disassemble it.
- if (Obj->isELF() && std::get<2>(Symbols[si]) == ELF::STT_OBJECT &&
+ if (Obj->isELF() && std::get<2>(Symbols[SI]) == ELF::STT_OBJECT &&
!DisassembleAll && Section.isText()) {
// print out data up to 8 bytes at a time in hex and ascii
uint8_t AsciiData[9] = {'\0'};
@@ -1675,6 +1754,14 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
if (Index >= End)
break;
+ if (size_t N =
+ countSkippableZeroBytes(Bytes.slice(Index, End - Index))) {
+ outs() << "\t\t..." << '\n';
+ Index += N;
+ if (Index >= End)
+ break;
+ }
+
// Disassemble a real instruction or a data when disassemble all is
// provided
bool Disassembled = DisAsm->getInstruction(Inst, Size, Bytes.slice(Index),
@@ -1753,32 +1840,32 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
// Hexagon does this in pretty printer
if (Obj->getArch() != Triple::hexagon)
// Print relocation for instruction.
- while (rel_cur != rel_end) {
- bool hidden = getHidden(*rel_cur);
- uint64_t addr = rel_cur->getOffset();
- SmallString<16> name;
- SmallString<32> val;
+ while (RelCur != RelEnd) {
+ uint64_t Addr = RelCur->getOffset();
+ SmallString<16> Name;
+ SmallString<32> Val;
// If this relocation is hidden, skip it.
- if (hidden || ((SectionAddr + addr) < StartAddress)) {
- ++rel_cur;
+ if (getHidden(*RelCur) || ((SectionAddr + Addr) < StartAddress)) {
+ ++RelCur;
continue;
}
// Stop when rel_cur's address is past the current instruction.
- if (addr >= Index + Size) break;
- rel_cur->getTypeName(name);
- error(getRelocationValueString(*rel_cur, val));
- outs() << format(Fmt.data(), SectionAddr + addr) << name
- << "\t" << val << "\n";
- ++rel_cur;
+ if (Addr >= Index + Size)
+ break;
+ RelCur->getTypeName(Name);
+ error(getRelocationValueString(*RelCur, Val));
+ outs() << format(Fmt.data(), SectionAddr + Addr) << Name << "\t"
+ << Val << "\n";
+ ++RelCur;
}
}
}
}
}
-void llvm::PrintRelocations(const ObjectFile *Obj) {
+void llvm::printRelocations(const ObjectFile *Obj) {
StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 :
"%08" PRIx64;
// Regular objdump doesn't print relocations in non-relocatable object
@@ -1789,61 +1876,57 @@ void llvm::PrintRelocations(const ObjectFile *Obj) {
for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
if (Section.relocation_begin() == Section.relocation_end())
continue;
- StringRef secname;
- error(Section.getName(secname));
- outs() << "RELOCATION RECORDS FOR [" << secname << "]:\n";
+ StringRef SecName;
+ error(Section.getName(SecName));
+ outs() << "RELOCATION RECORDS FOR [" << SecName << "]:\n";
for (const RelocationRef &Reloc : Section.relocations()) {
- bool hidden = getHidden(Reloc);
- uint64_t address = Reloc.getOffset();
- SmallString<32> relocname;
- SmallString<32> valuestr;
- if (address < StartAddress || address > StopAddress || hidden)
+ uint64_t Address = Reloc.getOffset();
+ SmallString<32> RelocName;
+ SmallString<32> ValueStr;
+ if (Address < StartAddress || Address > StopAddress || getHidden(Reloc))
continue;
- Reloc.getTypeName(relocname);
- error(getRelocationValueString(Reloc, valuestr));
- outs() << format(Fmt.data(), address) << " " << relocname << " "
- << valuestr << "\n";
+ Reloc.getTypeName(RelocName);
+ error(getRelocationValueString(Reloc, ValueStr));
+ outs() << format(Fmt.data(), Address) << " " << RelocName << " "
+ << ValueStr << "\n";
}
outs() << "\n";
}
}
-void llvm::PrintDynamicRelocations(const ObjectFile *Obj) {
-
+void llvm::printDynamicRelocations(const ObjectFile *Obj) {
// For the moment, this option is for ELF only
if (!Obj->isELF())
return;
const auto *Elf = dyn_cast<ELFObjectFileBase>(Obj);
-
if (!Elf || Elf->getEType() != ELF::ET_DYN) {
error("not a dynamic object");
return;
}
- StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
-
std::vector<SectionRef> DynRelSec = Obj->dynamic_relocation_sections();
if (DynRelSec.empty())
return;
outs() << "DYNAMIC RELOCATION RECORDS\n";
+ StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
for (const SectionRef &Section : DynRelSec) {
if (Section.relocation_begin() == Section.relocation_end())
continue;
for (const RelocationRef &Reloc : Section.relocations()) {
- uint64_t address = Reloc.getOffset();
- SmallString<32> relocname;
- SmallString<32> valuestr;
- Reloc.getTypeName(relocname);
- error(getRelocationValueString(Reloc, valuestr));
- outs() << format(Fmt.data(), address) << " " << relocname << " "
- << valuestr << "\n";
+ uint64_t Address = Reloc.getOffset();
+ SmallString<32> RelocName;
+ SmallString<32> ValueStr;
+ Reloc.getTypeName(RelocName);
+ error(getRelocationValueString(Reloc, ValueStr));
+ outs() << format(Fmt.data(), Address) << " " << RelocName << " "
+ << ValueStr << "\n";
}
}
}
-void llvm::PrintSectionHeaders(const ObjectFile *Obj) {
+void llvm::printSectionHeaders(const ObjectFile *Obj) {
outs() << "Sections:\n"
"Idx Name Size Address Type\n";
for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
@@ -1860,9 +1943,10 @@ void llvm::PrintSectionHeaders(const ObjectFile *Obj) {
(unsigned)Section.getIndex(), Name.str().c_str(), Size,
Address, Type.c_str());
}
+ outs() << "\n";
}
-void llvm::PrintSectionContents(const ObjectFile *Obj) {
+void llvm::printSectionContents(const ObjectFile *Obj) {
std::error_code EC;
for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
StringRef Name;
@@ -1884,23 +1968,23 @@ void llvm::PrintSectionContents(const ObjectFile *Obj) {
error(Section.getContents(Contents));
// Dump out the content as hex and printable ascii characters.
- for (std::size_t addr = 0, end = Contents.size(); addr < end; addr += 16) {
- outs() << format(" %04" PRIx64 " ", BaseAddr + addr);
+ for (std::size_t Addr = 0, End = Contents.size(); Addr < End; Addr += 16) {
+ outs() << format(" %04" PRIx64 " ", BaseAddr + Addr);
// Dump line of hex.
- for (std::size_t i = 0; i < 16; ++i) {
- if (i != 0 && i % 4 == 0)
+ for (std::size_t I = 0; I < 16; ++I) {
+ if (I != 0 && I % 4 == 0)
outs() << ' ';
- if (addr + i < end)
- outs() << hexdigit((Contents[addr + i] >> 4) & 0xF, true)
- << hexdigit(Contents[addr + i] & 0xF, true);
+ if (Addr + I < End)
+ outs() << hexdigit((Contents[Addr + I] >> 4) & 0xF, true)
+ << hexdigit(Contents[Addr + I] & 0xF, true);
else
outs() << " ";
}
// Print ascii.
outs() << " ";
- for (std::size_t i = 0; i < 16 && addr + i < end; ++i) {
- if (isPrint(static_cast<unsigned char>(Contents[addr + i]) & 0xFF))
- outs() << Contents[addr + i];
+ for (std::size_t I = 0; I < 16 && Addr + I < End; ++I) {
+ if (isPrint(static_cast<unsigned char>(Contents[Addr + I]) & 0xFF))
+ outs() << Contents[Addr + I];
else
outs() << ".";
}
@@ -1909,40 +1993,47 @@ void llvm::PrintSectionContents(const ObjectFile *Obj) {
}
}
-void llvm::PrintSymbolTable(const ObjectFile *o, StringRef ArchiveName,
+void llvm::printSymbolTable(const ObjectFile *O, StringRef ArchiveName,
StringRef ArchitectureName) {
outs() << "SYMBOL TABLE:\n";
- if (const COFFObjectFile *coff = dyn_cast<const COFFObjectFile>(o)) {
- printCOFFSymbolTable(coff);
+ if (const COFFObjectFile *Coff = dyn_cast<const COFFObjectFile>(O)) {
+ printCOFFSymbolTable(Coff);
return;
}
- for (const SymbolRef &Symbol : o->symbols()) {
+
+ for (auto I = O->symbol_begin(), E = O->symbol_end(); I != E; ++I) {
+ // Skip printing the special zero symbol when dumping an ELF file.
+ // This makes the output consistent with the GNU objdump.
+ if (I == O->symbol_begin() && isa<ELFObjectFileBase>(O))
+ continue;
+
+ const SymbolRef &Symbol = *I;
Expected<uint64_t> AddressOrError = Symbol.getAddress();
if (!AddressOrError)
- report_error(ArchiveName, o->getFileName(), AddressOrError.takeError(),
+ report_error(ArchiveName, O->getFileName(), AddressOrError.takeError(),
ArchitectureName);
uint64_t Address = *AddressOrError;
if ((Address < StartAddress) || (Address > StopAddress))
continue;
Expected<SymbolRef::Type> TypeOrError = Symbol.getType();
if (!TypeOrError)
- report_error(ArchiveName, o->getFileName(), TypeOrError.takeError(),
+ report_error(ArchiveName, O->getFileName(), TypeOrError.takeError(),
ArchitectureName);
SymbolRef::Type Type = *TypeOrError;
uint32_t Flags = Symbol.getFlags();
Expected<section_iterator> SectionOrErr = Symbol.getSection();
if (!SectionOrErr)
- report_error(ArchiveName, o->getFileName(), SectionOrErr.takeError(),
+ report_error(ArchiveName, O->getFileName(), SectionOrErr.takeError(),
ArchitectureName);
section_iterator Section = *SectionOrErr;
StringRef Name;
- if (Type == SymbolRef::ST_Debug && Section != o->section_end()) {
+ if (Type == SymbolRef::ST_Debug && Section != O->section_end()) {
Section->getName(Name);
} else {
Expected<StringRef> NameOrErr = Symbol.getName();
if (!NameOrErr)
- report_error(ArchiveName, o->getFileName(), NameOrErr.takeError(),
+ report_error(ArchiveName, O->getFileName(), NameOrErr.takeError(),
ArchitectureName);
Name = *NameOrErr;
}
@@ -1963,8 +2054,10 @@ void llvm::PrintSymbolTable(const ObjectFile *o, StringRef ArchiveName,
FileFunc = 'f';
else if (Type == SymbolRef::ST_Function)
FileFunc = 'F';
+ else if (Type == SymbolRef::ST_Data)
+ FileFunc = 'O';
- const char *Fmt = o->getBytesInAddress() > 4 ? "%016" PRIx64 :
+ const char *Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 :
"%08" PRIx64;
outs() << format(Fmt, Address) << " "
@@ -1980,11 +2073,11 @@ void llvm::PrintSymbolTable(const ObjectFile *o, StringRef ArchiveName,
outs() << "*ABS*";
} else if (Common) {
outs() << "*COM*";
- } else if (Section == o->section_end()) {
+ } else if (Section == O->section_end()) {
outs() << "*UND*";
} else {
if (const MachOObjectFile *MachO =
- dyn_cast<const MachOObjectFile>(o)) {
+ dyn_cast<const MachOObjectFile>(O)) {
DataRefImpl DR = Section->getRawDataRefImpl();
StringRef SegmentName = MachO->getSectionFinalSegmentName(DR);
outs() << SegmentName << ",";
@@ -1995,98 +2088,95 @@ void llvm::PrintSymbolTable(const ObjectFile *o, StringRef ArchiveName,
}
outs() << '\t';
- if (Common || isa<ELFObjectFileBase>(o)) {
+ if (Common || isa<ELFObjectFileBase>(O)) {
uint64_t Val =
Common ? Symbol.getAlignment() : ELFSymbolRef(Symbol).getSize();
outs() << format("\t %08" PRIx64 " ", Val);
}
- if (Hidden) {
+ if (Hidden)
outs() << ".hidden ";
- }
- outs() << Name
- << '\n';
+
+ if (Demangle)
+ outs() << demangle(Name) << '\n';
+ else
+ outs() << Name << '\n';
}
}
-static void PrintUnwindInfo(const ObjectFile *o) {
+static void printUnwindInfo(const ObjectFile *O) {
outs() << "Unwind info:\n\n";
- if (const COFFObjectFile *coff = dyn_cast<COFFObjectFile>(o)) {
- printCOFFUnwindInfo(coff);
- } else if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
+ if (const COFFObjectFile *Coff = dyn_cast<COFFObjectFile>(O))
+ printCOFFUnwindInfo(Coff);
+ else if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(O))
printMachOUnwindInfo(MachO);
- else {
+ else
// TODO: Extract DWARF dump tool to objdump.
- errs() << "This operation is only currently supported "
- "for COFF and MachO object files.\n";
- return;
- }
+ WithColor::error(errs(), ToolName)
+ << "This operation is only currently supported "
+ "for COFF and MachO object files.\n";
}
void llvm::printExportsTrie(const ObjectFile *o) {
outs() << "Exports trie:\n";
if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachOExportsTrie(MachO);
- else {
- errs() << "This operation is only currently supported "
- "for Mach-O executable files.\n";
- return;
- }
+ else
+ WithColor::error(errs(), ToolName)
+ << "This operation is only currently supported "
+ "for Mach-O executable files.\n";
}
void llvm::printRebaseTable(ObjectFile *o) {
outs() << "Rebase table:\n";
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachORebaseTable(MachO);
- else {
- errs() << "This operation is only currently supported "
- "for Mach-O executable files.\n";
- return;
- }
+ else
+ WithColor::error(errs(), ToolName)
+ << "This operation is only currently supported "
+ "for Mach-O executable files.\n";
}
void llvm::printBindTable(ObjectFile *o) {
outs() << "Bind table:\n";
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachOBindTable(MachO);
- else {
- errs() << "This operation is only currently supported "
- "for Mach-O executable files.\n";
- return;
- }
+ else
+ WithColor::error(errs(), ToolName)
+ << "This operation is only currently supported "
+ "for Mach-O executable files.\n";
}
void llvm::printLazyBindTable(ObjectFile *o) {
outs() << "Lazy bind table:\n";
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachOLazyBindTable(MachO);
- else {
- errs() << "This operation is only currently supported "
- "for Mach-O executable files.\n";
- return;
- }
+ else
+ WithColor::error(errs(), ToolName)
+ << "This operation is only currently supported "
+ "for Mach-O executable files.\n";
}
void llvm::printWeakBindTable(ObjectFile *o) {
outs() << "Weak bind table:\n";
if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
printMachOWeakBindTable(MachO);
- else {
- errs() << "This operation is only currently supported "
- "for Mach-O executable files.\n";
- return;
- }
+ else
+ WithColor::error(errs(), ToolName)
+ << "This operation is only currently supported "
+ "for Mach-O executable files.\n";
}
/// Dump the raw contents of the __clangast section so the output can be piped
/// into llvm-bcanalyzer.
void llvm::printRawClangAST(const ObjectFile *Obj) {
if (outs().is_displayed()) {
- errs() << "The -raw-clang-ast option will dump the raw binary contents of "
- "the clang ast section.\n"
- "Please redirect the output to a file or another program such as "
- "llvm-bcanalyzer.\n";
+ WithColor::error(errs(), ToolName)
+ << "The -raw-clang-ast option will dump the raw binary contents of "
+ "the clang ast section.\n"
+ "Please redirect the output to a file or another program such as "
+ "llvm-bcanalyzer.\n";
return;
}
@@ -2113,15 +2203,16 @@ void llvm::printRawClangAST(const ObjectFile *Obj) {
}
static void printFaultMaps(const ObjectFile *Obj) {
- const char *FaultMapSectionName = nullptr;
+ StringRef FaultMapSectionName;
if (isa<ELFObjectFileBase>(Obj)) {
FaultMapSectionName = ".llvm_faultmaps";
} else if (isa<MachOObjectFile>(Obj)) {
FaultMapSectionName = "__llvm_faultmaps";
} else {
- errs() << "This operation is only currently supported "
- "for ELF and Mach-O executable files.\n";
+ WithColor::error(errs(), ToolName)
+ << "This operation is only currently supported "
+ "for ELF and Mach-O executable files.\n";
return;
}
@@ -2152,42 +2243,44 @@ static void printFaultMaps(const ObjectFile *Obj) {
outs() << FMP;
}
-static void printPrivateFileHeaders(const ObjectFile *o, bool onlyFirst) {
- if (o->isELF()) {
- printELFFileHeader(o);
- return printELFDynamicSection(o);
+static void printPrivateFileHeaders(const ObjectFile *O, bool OnlyFirst) {
+ if (O->isELF()) {
+ printELFFileHeader(O);
+ return printELFDynamicSection(O);
}
- if (o->isCOFF())
- return printCOFFFileHeader(o);
- if (o->isWasm())
- return printWasmFileHeader(o);
- if (o->isMachO()) {
- printMachOFileHeader(o);
- if (!onlyFirst)
- printMachOLoadCommands(o);
+ if (O->isCOFF())
+ return printCOFFFileHeader(O);
+ if (O->isWasm())
+ return printWasmFileHeader(O);
+ if (O->isMachO()) {
+ printMachOFileHeader(O);
+ if (!OnlyFirst)
+ printMachOLoadCommands(O);
return;
}
- report_error(o->getFileName(), "Invalid/Unsupported object file format");
+ report_error(O->getFileName(), "Invalid/Unsupported object file format");
}
-static void printFileHeaders(const ObjectFile *o) {
- if (!o->isELF() && !o->isCOFF())
- report_error(o->getFileName(), "Invalid/Unsupported object file format");
+static void printFileHeaders(const ObjectFile *O) {
+ if (!O->isELF() && !O->isCOFF())
+ report_error(O->getFileName(), "Invalid/Unsupported object file format");
- Triple::ArchType AT = o->getArch();
+ Triple::ArchType AT = O->getArch();
outs() << "architecture: " << Triple::getArchTypeName(AT) << "\n";
- Expected<uint64_t> StartAddrOrErr = o->getStartAddress();
+ Expected<uint64_t> StartAddrOrErr = O->getStartAddress();
if (!StartAddrOrErr)
- report_error(o->getFileName(), StartAddrOrErr.takeError());
+ report_error(O->getFileName(), StartAddrOrErr.takeError());
+
+ StringRef Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
+ uint64_t Address = StartAddrOrErr.get();
outs() << "start address: "
- << format("0x%0*x", o->getBytesInAddress(), StartAddrOrErr.get())
- << "\n";
+ << "0x" << format(Fmt.data(), Address) << "\n\n";
}
static void printArchiveChild(StringRef Filename, const Archive::Child &C) {
Expected<sys::fs::perms> ModeOrErr = C.getAccessMode();
if (!ModeOrErr) {
- errs() << "ill-formed archive entry.\n";
+ WithColor::error(errs(), ToolName) << "ill-formed archive entry.\n";
consumeError(ModeOrErr.takeError());
return;
}
@@ -2248,55 +2341,55 @@ static void printArchiveChild(StringRef Filename, const Archive::Child &C) {
outs() << Name << "\n";
}
-static void DumpObject(ObjectFile *o, const Archive *a = nullptr,
- const Archive::Child *c = nullptr) {
- StringRef ArchiveName = a != nullptr ? a->getFileName() : "";
+static void dumpObject(ObjectFile *O, const Archive *A = nullptr,
+ const Archive::Child *C = nullptr) {
// Avoid other output when using a raw option.
if (!RawClangAST) {
outs() << '\n';
- if (a)
- outs() << a->getFileName() << "(" << o->getFileName() << ")";
+ if (A)
+ outs() << A->getFileName() << "(" << O->getFileName() << ")";
else
- outs() << o->getFileName();
- outs() << ":\tfile format " << o->getFileFormatName() << "\n\n";
+ outs() << O->getFileName();
+ outs() << ":\tfile format " << O->getFileFormatName() << "\n\n";
}
- if (ArchiveHeaders && !MachOOpt)
- printArchiveChild(a->getFileName(), *c);
+ StringRef ArchiveName = A ? A->getFileName() : "";
+ if (FileHeaders)
+ printFileHeaders(O);
+ if (ArchiveHeaders && !MachOOpt && C)
+ printArchiveChild(ArchiveName, *C);
if (Disassemble)
- DisassembleObject(o, Relocations);
+ disassembleObject(O, Relocations);
if (Relocations && !Disassemble)
- PrintRelocations(o);
+ printRelocations(O);
if (DynamicRelocations)
- PrintDynamicRelocations(o);
+ printDynamicRelocations(O);
if (SectionHeaders)
- PrintSectionHeaders(o);
+ printSectionHeaders(O);
if (SectionContents)
- PrintSectionContents(o);
+ printSectionContents(O);
if (SymbolTable)
- PrintSymbolTable(o, ArchiveName);
+ printSymbolTable(O, ArchiveName);
if (UnwindInfo)
- PrintUnwindInfo(o);
+ printUnwindInfo(O);
if (PrivateHeaders || FirstPrivateHeader)
- printPrivateFileHeaders(o, FirstPrivateHeader);
- if (FileHeaders)
- printFileHeaders(o);
+ printPrivateFileHeaders(O, FirstPrivateHeader);
if (ExportsTrie)
- printExportsTrie(o);
+ printExportsTrie(O);
if (Rebase)
- printRebaseTable(o);
+ printRebaseTable(O);
if (Bind)
- printBindTable(o);
+ printBindTable(O);
if (LazyBind)
- printLazyBindTable(o);
+ printLazyBindTable(O);
if (WeakBind)
- printWeakBindTable(o);
+ printWeakBindTable(O);
if (RawClangAST)
- printRawClangAST(o);
+ printRawClangAST(O);
if (PrintFaultMaps)
- printFaultMaps(o);
+ printFaultMaps(O);
if (DwarfDumpType != DIDT_Null) {
- std::unique_ptr<DIContext> DICtx = DWARFContext::create(*o);
+ std::unique_ptr<DIContext> DICtx = DWARFContext::create(*O);
// Dump the complete DWARF structure.
DIDumpOptions DumpOpts;
DumpOpts.DumpType = DwarfDumpType;
@@ -2304,7 +2397,7 @@ static void DumpObject(ObjectFile *o, const Archive *a = nullptr,
}
}
-static void DumpObject(const COFFImportFile *I, const Archive *A,
+static void dumpObject(const COFFImportFile *I, const Archive *A,
const Archive::Child *C = nullptr) {
StringRef ArchiveName = A ? A->getFileName() : "";
@@ -2315,41 +2408,40 @@ static void DumpObject(const COFFImportFile *I, const Archive *A,
<< ":\tfile format COFF-import-file"
<< "\n\n";
- if (ArchiveHeaders && !MachOOpt)
- printArchiveChild(A->getFileName(), *C);
+ if (ArchiveHeaders && !MachOOpt && C)
+ printArchiveChild(ArchiveName, *C);
if (SymbolTable)
printCOFFSymbolTable(I);
}
/// Dump each object file in \a a;
-static void DumpArchive(const Archive *a) {
+static void dumpArchive(const Archive *A) {
Error Err = Error::success();
- for (auto &C : a->children(Err)) {
+ for (auto &C : A->children(Err)) {
Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
if (!ChildOrErr) {
if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
- report_error(a->getFileName(), C, std::move(E));
+ report_error(A->getFileName(), C, std::move(E));
continue;
}
- if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get()))
- DumpObject(o, a, &C);
+ if (ObjectFile *O = dyn_cast<ObjectFile>(&*ChildOrErr.get()))
+ dumpObject(O, A, &C);
else if (COFFImportFile *I = dyn_cast<COFFImportFile>(&*ChildOrErr.get()))
- DumpObject(I, a, &C);
+ dumpObject(I, A, &C);
else
- report_error(a->getFileName(), object_error::invalid_file_type);
+ report_error(A->getFileName(), object_error::invalid_file_type);
}
if (Err)
- report_error(a->getFileName(), std::move(Err));
+ report_error(A->getFileName(), std::move(Err));
}
/// Open file and figure out how to dump it.
-static void DumpInput(StringRef file) {
-
+static void dumpInput(StringRef file) {
// If we are using the Mach-O specific object file parser, then let it parse
// the file and process the command line options. So the -arch flags can
// be used to select specific slices, etc.
if (MachOOpt) {
- ParseInputMachO(file);
+ parseInputMachO(file);
return;
}
@@ -2359,10 +2451,12 @@ static void DumpInput(StringRef file) {
report_error(file, BinaryOrErr.takeError());
Binary &Binary = *BinaryOrErr.get().getBinary();
- if (Archive *a = dyn_cast<Archive>(&Binary))
- DumpArchive(a);
- else if (ObjectFile *o = dyn_cast<ObjectFile>(&Binary))
- DumpObject(o);
+ if (Archive *A = dyn_cast<Archive>(&Binary))
+ dumpArchive(A);
+ else if (ObjectFile *O = dyn_cast<ObjectFile>(&Binary))
+ dumpObject(O);
+ else if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Binary))
+ parseInputMachO(UB);
else
report_error(file, object_error::invalid_file_type);
}
@@ -2379,24 +2473,20 @@ int main(int argc, char **argv) {
cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
cl::ParseCommandLineOptions(argc, argv, "llvm object file dumper\n");
- TripleName = Triple::normalize(TripleName);
ToolName = argv[0];
// Defaults to a.out if no filenames specified.
- if (InputFilenames.size() == 0)
+ if (InputFilenames.empty())
InputFilenames.push_back("a.out");
if (AllHeaders)
- PrivateHeaders = Relocations = SectionHeaders = SymbolTable = true;
+ FileHeaders = PrivateHeaders = Relocations = SectionHeaders = SymbolTable =
+ true;
if (DisassembleAll || PrintSource || PrintLines)
Disassemble = true;
- if (Demangle.getValue() != "none" && Demangle.getValue() != "" &&
- Demangle.getValue() != "itanium")
- warn("Unsupported demangling style");
-
if (!Disassemble
&& !Relocations
&& !DynamicRelocations
@@ -2422,7 +2512,7 @@ int main(int argc, char **argv) {
&& !(DylibsUsed && MachOOpt)
&& !(DylibId && MachOOpt)
&& !(ObjcMetaData && MachOOpt)
- && !(FilterSections.size() != 0 && MachOOpt)
+ && !(!FilterSections.empty() && MachOOpt)
&& !PrintFaultMaps
&& DwarfDumpType == DIDT_Null) {
cl::PrintHelpMessage();
@@ -2432,7 +2522,7 @@ int main(int argc, char **argv) {
DisasmFuncsSet.insert(DisassembleFunctions.begin(),
DisassembleFunctions.end());
- llvm::for_each(InputFilenames, DumpInput);
+ llvm::for_each(InputFilenames, dumpInput);
return EXIT_SUCCESS;
}
diff --git a/tools/llvm-objdump/llvm-objdump.h b/tools/llvm-objdump/llvm-objdump.h
index b2eb6e9d7771..fe2cb05fe227 100644
--- a/tools/llvm-objdump/llvm-objdump.h
+++ b/tools/llvm-objdump/llvm-objdump.h
@@ -22,6 +22,7 @@ namespace object {
class COFFObjectFile;
class COFFImportFile;
class MachOObjectFile;
+ class MachOUniversalBinary;
class ObjectFile;
class Archive;
class RelocationRef;
@@ -30,10 +31,10 @@ namespace object {
extern cl::opt<std::string> TripleName;
extern cl::opt<std::string> ArchName;
extern cl::opt<std::string> MCPU;
-extern cl::opt<std::string> Demangle;
extern cl::list<std::string> MAttrs;
extern cl::list<std::string> FilterSections;
extern cl::opt<bool> AllHeaders;
+extern cl::opt<bool> Demangle;
extern cl::opt<bool> Disassemble;
extern cl::opt<bool> DisassembleAll;
extern cl::opt<bool> NoShowRawInsn;
@@ -69,34 +70,35 @@ extern cl::opt<DIDumpType> DwarfDumpType;
// Various helper functions.
void error(std::error_code ec);
-bool RelocAddressLess(object::RelocationRef a, object::RelocationRef b);
-void ParseInputMachO(StringRef Filename);
-void printCOFFUnwindInfo(const object::COFFObjectFile* o);
-void printMachOUnwindInfo(const object::MachOObjectFile* o);
-void printMachOExportsTrie(const object::MachOObjectFile* o);
-void printMachORebaseTable(object::MachOObjectFile* o);
-void printMachOBindTable(object::MachOObjectFile* o);
-void printMachOLazyBindTable(object::MachOObjectFile* o);
-void printMachOWeakBindTable(object::MachOObjectFile* o);
-void printELFFileHeader(const object::ObjectFile *o);
+bool isRelocAddressLess(object::RelocationRef A, object::RelocationRef B);
+void parseInputMachO(StringRef Filename);
+void parseInputMachO(object::MachOUniversalBinary *UB);
+void printCOFFUnwindInfo(const object::COFFObjectFile *O);
+void printMachOUnwindInfo(const object::MachOObjectFile *O);
+void printMachOExportsTrie(const object::MachOObjectFile *O);
+void printMachORebaseTable(object::MachOObjectFile *O);
+void printMachOBindTable(object::MachOObjectFile *O);
+void printMachOLazyBindTable(object::MachOObjectFile *O);
+void printMachOWeakBindTable(object::MachOObjectFile *O);
+void printELFFileHeader(const object::ObjectFile *O);
void printELFDynamicSection(const object::ObjectFile *Obj);
-void printCOFFFileHeader(const object::ObjectFile *o);
-void printCOFFSymbolTable(const object::COFFImportFile *i);
-void printCOFFSymbolTable(const object::COFFObjectFile *o);
-void printMachOFileHeader(const object::ObjectFile *o);
-void printMachOLoadCommands(const object::ObjectFile *o);
-void printWasmFileHeader(const object::ObjectFile *o);
-void printExportsTrie(const object::ObjectFile *o);
-void printRebaseTable(object::ObjectFile *o);
-void printBindTable(object::ObjectFile *o);
-void printLazyBindTable(object::ObjectFile *o);
-void printWeakBindTable(object::ObjectFile *o);
-void printRawClangAST(const object::ObjectFile *o);
-void PrintRelocations(const object::ObjectFile *o);
-void PrintDynamicRelocations(const object::ObjectFile *o);
-void PrintSectionHeaders(const object::ObjectFile *o);
-void PrintSectionContents(const object::ObjectFile *o);
-void PrintSymbolTable(const object::ObjectFile *o, StringRef ArchiveName,
+void printCOFFFileHeader(const object::ObjectFile *O);
+void printCOFFSymbolTable(const object::COFFImportFile *I);
+void printCOFFSymbolTable(const object::COFFObjectFile *O);
+void printMachOFileHeader(const object::ObjectFile *O);
+void printMachOLoadCommands(const object::ObjectFile *O);
+void printWasmFileHeader(const object::ObjectFile *O);
+void printExportsTrie(const object::ObjectFile *O);
+void printRebaseTable(object::ObjectFile *O);
+void printBindTable(object::ObjectFile *O);
+void printLazyBindTable(object::ObjectFile *O);
+void printWeakBindTable(object::ObjectFile *O);
+void printRawClangAST(const object::ObjectFile *O);
+void printRelocations(const object::ObjectFile *O);
+void printDynamicRelocations(const object::ObjectFile *O);
+void printSectionHeaders(const object::ObjectFile *O);
+void printSectionContents(const object::ObjectFile *O);
+void printSymbolTable(const object::ObjectFile *O, StringRef ArchiveName,
StringRef ArchitectureName = StringRef());
void warn(StringRef Message);
LLVM_ATTRIBUTE_NORETURN void error(Twine Message);
diff --git a/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp b/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp
index 98d5428ddd1a..57e75b1db9ec 100644
--- a/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp
+++ b/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp
@@ -144,9 +144,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
PB.registerLoopAnalyses(LAM);
PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
- bool Ok = PB.parsePassPipeline(MPM, PassPipeline, false, false);
- assert(Ok && "Should have been checked during fuzzer initialization");
- (void)Ok; // silence unused variable warning on release builds
+ auto Err = PB.parsePassPipeline(MPM, PassPipeline, false, false);
+ assert(!Err && "Should have been checked during fuzzer initialization");
+ // Only fail with assert above, otherwise ignore the parsing error.
+ consumeError(std::move(Err));
// Run passes which we need to test
//
@@ -235,8 +236,8 @@ extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize(
PassBuilder PB(TM.get());
ModulePassManager MPM;
- if (!PB.parsePassPipeline(MPM, PassPipeline, false, false)) {
- errs() << *argv[0] << ": can't parse pass pipeline\n";
+ if (auto Err = PB.parsePassPipeline(MPM, PassPipeline, false, false)) {
+ errs() << *argv[0] << ": " << toString(std::move(Err)) << "\n";
exit(1);
}
diff --git a/tools/llvm-opt-report/CMakeLists.txt b/tools/llvm-opt-report/CMakeLists.txt
index 777537a54c0f..3aabc03ab3f2 100644
--- a/tools/llvm-opt-report/CMakeLists.txt
+++ b/tools/llvm-opt-report/CMakeLists.txt
@@ -1,4 +1,4 @@
-set(LLVM_LINK_COMPONENTS Core Demangle Object Support)
+set(LLVM_LINK_COMPONENTS Core Demangle Object OptRemarks Support)
add_llvm_tool(llvm-opt-report
OptReport.cpp
diff --git a/tools/llvm-opt-report/OptReport.cpp b/tools/llvm-opt-report/OptReport.cpp
index aa7966132c28..0c4bc94d8e44 100644
--- a/tools/llvm-opt-report/OptReport.cpp
+++ b/tools/llvm-opt-report/OptReport.cpp
@@ -28,6 +28,7 @@
#include "llvm/Support/WithColor.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm-c/OptRemarks.h"
#include <cstdlib>
#include <map>
#include <set>
@@ -142,104 +143,44 @@ typedef std::map<std::string, std::map<int, std::map<std::string, std::map<int,
OptReportLocationInfo>>>> LocationInfoTy;
} // anonymous namespace
-static void collectLocationInfo(yaml::Stream &Stream,
- LocationInfoTy &LocationInfo) {
- SmallVector<char, 8> Tmp;
-
- // Note: We're using the YAML parser here directly, instead of using the
- // YAMLTraits implementation, because the YAMLTraits implementation does not
- // support a way to handle only a subset of the input keys (it will error out
- // if there is an input key that you don't map to your class), and
- // furthermore, it does not provide a way to handle the Args sequence of
- // key/value pairs, where the order must be captured and the 'String' key
- // might be repeated.
- for (auto &Doc : Stream) {
- auto *Root = dyn_cast<yaml::MappingNode>(Doc.getRoot());
- if (!Root)
- continue;
+static bool readLocationInfo(LocationInfoTy &LocationInfo) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
+ MemoryBuffer::getFile(InputFileName.c_str());
+ if (std::error_code EC = Buf.getError()) {
+ WithColor::error() << "Can't open file " << InputFileName << ": "
+ << EC.message() << "\n";
+ return false;
+ }
- bool Transformed = Root->getRawTag() == "!Passed";
- std::string Pass, File, Function;
- int Line = 0, Column = 1;
+ StringRef Buffer = (*Buf)->getBuffer();
+ LLVMOptRemarkParserRef Parser =
+ LLVMOptRemarkParserCreate(Buffer.data(), Buffer.size());
+
+ LLVMOptRemarkEntry *Remark = nullptr;
+ while ((Remark = LLVMOptRemarkParserGetNext(Parser))) {
+ bool Transformed =
+ StringRef(Remark->RemarkType.Str, Remark->RemarkType.Len) == "!Passed";
+ StringRef Pass(Remark->PassName.Str, Remark->PassName.Len);
+ StringRef File(Remark->DebugLoc.SourceFile.Str,
+ Remark->DebugLoc.SourceFile.Len);
+ StringRef Function(Remark->FunctionName.Str, Remark->FunctionName.Len);
+ uint32_t Line = Remark->DebugLoc.SourceLineNumber;
+ uint32_t Column = Remark->DebugLoc.SourceColumnNumber;
+ ArrayRef<LLVMOptRemarkArg> Args(Remark->Args, Remark->NumArgs);
int VectorizationFactor = 1;
int InterleaveCount = 1;
int UnrollCount = 1;
- for (auto &RootChild : *Root) {
- auto *Key = dyn_cast<yaml::ScalarNode>(RootChild.getKey());
- if (!Key)
- continue;
- StringRef KeyName = Key->getValue(Tmp);
- if (KeyName == "Pass") {
- auto *Value = dyn_cast<yaml::ScalarNode>(RootChild.getValue());
- if (!Value)
- continue;
- Pass = Value->getValue(Tmp);
- } else if (KeyName == "Function") {
- auto *Value = dyn_cast<yaml::ScalarNode>(RootChild.getValue());
- if (!Value)
- continue;
- Function = Value->getValue(Tmp);
- } else if (KeyName == "DebugLoc") {
- auto *DebugLoc = dyn_cast<yaml::MappingNode>(RootChild.getValue());
- if (!DebugLoc)
- continue;
-
- for (auto &DLChild : *DebugLoc) {
- auto *DLKey = dyn_cast<yaml::ScalarNode>(DLChild.getKey());
- if (!DLKey)
- continue;
- StringRef DLKeyName = DLKey->getValue(Tmp);
- if (DLKeyName == "File") {
- auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue());
- if (!Value)
- continue;
- File = Value->getValue(Tmp);
- } else if (DLKeyName == "Line") {
- auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue());
- if (!Value)
- continue;
- Value->getValue(Tmp).getAsInteger(10, Line);
- } else if (DLKeyName == "Column") {
- auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue());
- if (!Value)
- continue;
- Value->getValue(Tmp).getAsInteger(10, Column);
- }
- }
- } else if (KeyName == "Args") {
- auto *Args = dyn_cast<yaml::SequenceNode>(RootChild.getValue());
- if (!Args)
- continue;
- for (auto &ArgChild : *Args) {
- auto *ArgMap = dyn_cast<yaml::MappingNode>(&ArgChild);
- if (!ArgMap)
- continue;
- for (auto &ArgKV : *ArgMap) {
- auto *ArgKey = dyn_cast<yaml::ScalarNode>(ArgKV.getKey());
- if (!ArgKey)
- continue;
- StringRef ArgKeyName = ArgKey->getValue(Tmp);
- if (ArgKeyName == "VectorizationFactor") {
- auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue());
- if (!Value)
- continue;
- Value->getValue(Tmp).getAsInteger(10, VectorizationFactor);
- } else if (ArgKeyName == "InterleaveCount") {
- auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue());
- if (!Value)
- continue;
- Value->getValue(Tmp).getAsInteger(10, InterleaveCount);
- } else if (ArgKeyName == "UnrollCount") {
- auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue());
- if (!Value)
- continue;
- Value->getValue(Tmp).getAsInteger(10, UnrollCount);
- }
- }
- }
- }
+ for (const LLVMOptRemarkArg &Arg : Args) {
+ StringRef ArgKeyName(Arg.Key.Str, Arg.Key.Len);
+ StringRef ArgValue(Arg.Value.Str, Arg.Value.Len);
+ if (ArgKeyName == "VectorizationFactor")
+ ArgValue.getAsInteger(10, VectorizationFactor);
+ else if (ArgKeyName == "InterleaveCount")
+ ArgValue.getAsInteger(10, InterleaveCount);
+ else if (ArgKeyName == "UnrollCount")
+ ArgValue.getAsInteger(10, UnrollCount);
}
if (Line < 1 || File.empty())
@@ -268,22 +209,13 @@ static void collectLocationInfo(yaml::Stream &Stream,
UpdateLLII(LI.Vectorized);
}
}
-}
-static bool readLocationInfo(LocationInfoTy &LocationInfo) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
- MemoryBuffer::getFileOrSTDIN(InputFileName);
- if (std::error_code EC = Buf.getError()) {
- WithColor::error() << "Can't open file " << InputFileName << ": "
- << EC.message() << "\n";
- return false;
- }
-
- SourceMgr SM;
- yaml::Stream Stream(Buf.get()->getBuffer(), SM);
- collectLocationInfo(Stream, LocationInfo);
+ bool HasError = LLVMOptRemarkParserHasError(Parser);
+ if (HasError)
+ WithColor::error() << LLVMOptRemarkParserGetErrorMessage(Parser) << "\n";
- return true;
+ LLVMOptRemarkParserDispose(Parser);
+ return !HasError;
}
static bool writeReport(LocationInfoTy &LocationInfo) {
@@ -299,13 +231,8 @@ static bool writeReport(LocationInfoTy &LocationInfo) {
bool FirstFile = true;
for (auto &FI : LocationInfo) {
SmallString<128> FileName(FI.first);
- if (!InputRelDir.empty()) {
- if (std::error_code EC = sys::fs::make_absolute(InputRelDir, FileName)) {
- WithColor::error() << "Can't resolve file path to " << FileName << ": "
- << EC.message() << "\n";
- return false;
- }
- }
+ if (!InputRelDir.empty())
+ sys::fs::make_absolute(InputRelDir, FileName);
const auto &FileInfo = FI.second;
diff --git a/tools/llvm-pdbutil/Analyze.cpp b/tools/llvm-pdbutil/Analyze.cpp
deleted file mode 100644
index 974ab49d9440..000000000000
--- a/tools/llvm-pdbutil/Analyze.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-//===- Analyze.cpp - PDB analysis functions ---------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "Analyze.h"
-
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
-#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
-#include "llvm/DebugInfo/CodeView/TypeRecord.h"
-#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
-#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
-#include "llvm/DebugInfo/PDB/Native/RawError.h"
-#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
-
-#include "llvm/Support/FormatVariadic.h"
-#include "llvm/Support/raw_ostream.h"
-
-#include <list>
-
-using namespace llvm;
-using namespace llvm::codeview;
-using namespace llvm::pdb;
-
-static StringRef getLeafTypeName(TypeLeafKind LT) {
- switch (LT) {
-#define TYPE_RECORD(ename, value, name) \
- case ename: \
- return #name;
-#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
- default:
- break;
- }
- return "UnknownLeaf";
-}
-
-namespace {
-struct HashLookupVisitor : public TypeVisitorCallbacks {
- struct Entry {
- TypeIndex TI;
- CVType Record;
- };
-
- explicit HashLookupVisitor(TpiStream &Tpi) : Tpi(Tpi) {}
-
- Error visitTypeBegin(CVType &Record) override {
- uint32_t H = Tpi.getHashValues()[I];
- Record.Hash = H;
- TypeIndex TI(I + TypeIndex::FirstNonSimpleIndex);
- Lookup[H].push_back(Entry{TI, Record});
- ++I;
- return Error::success();
- }
-
- uint32_t I = 0;
- DenseMap<uint32_t, std::list<Entry>> Lookup;
- TpiStream &Tpi;
-};
-}
-
-AnalysisStyle::AnalysisStyle(PDBFile &File) : File(File) {}
-
-Error AnalysisStyle::dump() {
- auto Tpi = File.getPDBTpiStream();
- if (!Tpi)
- return Tpi.takeError();
-
- HashLookupVisitor Hasher(*Tpi);
-
- uint32_t RecordCount = Tpi->getNumTypeRecords();
- auto Offsets = Tpi->getTypeIndexOffsets();
- auto Types = llvm::make_unique<LazyRandomTypeCollection>(
- Tpi->typeArray(), RecordCount, Offsets);
-
- if (auto EC = codeview::visitTypeStream(*Types, Hasher))
- return EC;
-
- auto &Adjusters = Tpi->getHashAdjusters();
- DenseSet<uint32_t> AdjusterSet;
- for (const auto &Adj : Adjusters) {
- assert(AdjusterSet.find(Adj.second) == AdjusterSet.end());
- AdjusterSet.insert(Adj.second);
- }
-
- uint32_t Count = 0;
- outs() << "Searching for hash collisions\n";
- for (const auto &H : Hasher.Lookup) {
- if (H.second.size() <= 1)
- continue;
- ++Count;
- outs() << formatv("Hash: {0}, Count: {1} records\n", H.first,
- H.second.size());
- for (const auto &R : H.second) {
- auto Iter = AdjusterSet.find(R.TI.getIndex());
- StringRef Prefix;
- if (Iter != AdjusterSet.end()) {
- Prefix = "[HEAD]";
- AdjusterSet.erase(Iter);
- }
- StringRef LeafName = getLeafTypeName(R.Record.Type);
- uint32_t TI = R.TI.getIndex();
- StringRef TypeName = Types->getTypeName(R.TI);
- outs() << formatv("{0,-6} {1} ({2:x}) {3}\n", Prefix, LeafName, TI,
- TypeName);
- }
- }
-
- outs() << "\n";
- outs() << "Dumping hash adjustment chains\n";
- for (const auto &A : Tpi->getHashAdjusters()) {
- TypeIndex TI(A.second);
- StringRef TypeName = Types->getTypeName(TI);
- const CVType &HeadRecord = Types->getType(TI);
- assert(HeadRecord.Hash.hasValue());
-
- auto CollisionsIter = Hasher.Lookup.find(*HeadRecord.Hash);
- if (CollisionsIter == Hasher.Lookup.end())
- continue;
-
- const auto &Collisions = CollisionsIter->second;
- outs() << TypeName << "\n";
- outs() << formatv(" [HEAD] {0:x} {1} {2}\n", uint32_t(A.second),
- getLeafTypeName(HeadRecord.Type), TypeName);
- for (const auto &Chain : Collisions) {
- if (Chain.TI == TI)
- continue;
- const CVType &TailRecord = Types->getType(Chain.TI);
- outs() << formatv(" {0:x} {1} {2}\n", Chain.TI.getIndex(),
- getLeafTypeName(TailRecord.Type),
- Types->getTypeName(Chain.TI));
- }
- }
- outs() << formatv("There are {0} orphaned hash adjusters\n",
- AdjusterSet.size());
- for (const auto &Adj : AdjusterSet) {
- outs() << formatv(" {0}\n", Adj);
- }
-
- uint32_t DistinctHashValues = Hasher.Lookup.size();
- outs() << formatv("{0}/{1} hash collisions", Count, DistinctHashValues);
- return Error::success();
-}
diff --git a/tools/llvm-pdbutil/Analyze.h b/tools/llvm-pdbutil/Analyze.h
deleted file mode 100644
index 7230ae45b0c8..000000000000
--- a/tools/llvm-pdbutil/Analyze.h
+++ /dev/null
@@ -1,30 +0,0 @@
-//===- Analyze.h - PDB analysis functions -----------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVMPDBDUMP_ANALYSIS_H
-#define LLVM_TOOLS_LLVMPDBDUMP_ANALYSIS_H
-
-#include "OutputStyle.h"
-
-namespace llvm {
-namespace pdb {
-class PDBFile;
-class AnalysisStyle : public OutputStyle {
-public:
- explicit AnalysisStyle(PDBFile &File);
-
- Error dump() override;
-
-private:
- PDBFile &File;
-};
-}
-}
-
-#endif
diff --git a/tools/llvm-pdbutil/CMakeLists.txt b/tools/llvm-pdbutil/CMakeLists.txt
index 1ccbfdfc4df4..e403d54eef58 100644
--- a/tools/llvm-pdbutil/CMakeLists.txt
+++ b/tools/llvm-pdbutil/CMakeLists.txt
@@ -9,7 +9,6 @@ set(LLVM_LINK_COMPONENTS
)
add_llvm_tool(llvm-pdbutil
- Analyze.cpp
BytesOutputStyle.cpp
DumpOutputStyle.cpp
ExplainOutputStyle.cpp
diff --git a/tools/llvm-pdbutil/DumpOutputStyle.cpp b/tools/llvm-pdbutil/DumpOutputStyle.cpp
index 9e59adc71967..e4f6aa7f6ec5 100644
--- a/tools/llvm-pdbutil/DumpOutputStyle.cpp
+++ b/tools/llvm-pdbutil/DumpOutputStyle.cpp
@@ -22,6 +22,7 @@
#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h"
#include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h"
#include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h"
+#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"
#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h"
#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h"
#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h"
@@ -65,6 +66,16 @@ DumpOutputStyle::DumpOutputStyle(InputFile &File)
PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); }
object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); }
+void DumpOutputStyle::printStreamNotValidForObj() {
+ AutoIndent Indent(P, 4);
+ P.formatLine("Dumping this stream is not valid for object files");
+}
+
+void DumpOutputStyle::printStreamNotPresent(StringRef StreamName) {
+ AutoIndent Indent(P, 4);
+ P.formatLine("{0} stream not present", StreamName);
+}
+
Error DumpOutputStyle::dump() {
if (opts::dump::DumpSummary) {
if (auto EC = dumpFileSummary())
@@ -132,6 +143,11 @@ Error DumpOutputStyle::dump() {
return EC;
}
+ if (opts::dump::DumpFpo) {
+ if (auto EC = dumpFpo())
+ return EC;
+ }
+
if (File.isObj()) {
if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() ||
opts::dump::DumpTypeExtras)
@@ -199,14 +215,14 @@ static void printHeader(LinePrinter &P, const Twine &S) {
Error DumpOutputStyle::dumpFileSummary() {
printHeader(P, "Summary");
- ExitOnError Err("Invalid PDB Format: ");
-
- AutoIndent Indent(P);
if (File.isObj()) {
- P.formatLine("Dumping File summary is not valid for object files");
+ printStreamNotValidForObj();
return Error::success();
}
+ AutoIndent Indent(P);
+ ExitOnError Err("Invalid PDB Format: ");
+
P.formatLine("Block Size: {0}", getPdb().getBlockSize());
P.formatLine("Number of blocks: {0}", getPdb().getBlockCount());
P.formatLine("Number of streams: {0}", getPdb().getNumStreams());
@@ -234,7 +250,7 @@ Error DumpOutputStyle::dumpFileSummary() {
static StatCollection getSymbolStats(const SymbolGroup &SG,
StatCollection &CumulativeStats) {
StatCollection Stats;
- if (SG.getFile().isPdb()) {
+ if (SG.getFile().isPdb() && SG.hasDebugStream()) {
// For PDB files, all symbols are packed into one stream.
for (const auto &S : SG.getPdbModuleStream().symbols(nullptr)) {
Stats.update(S.kind(), S.length());
@@ -326,12 +342,13 @@ static bool shouldDumpSymbolGroup(uint32_t Idx, const SymbolGroup &Group) {
Error DumpOutputStyle::dumpStreamSummary() {
printHeader(P, "Streams");
- AutoIndent Indent(P);
if (File.isObj()) {
- P.formatLine("Dumping streams is not valid for object files");
+ printStreamNotValidForObj();
return Error::success();
}
+ AutoIndent Indent(P);
+
if (StreamPurposes.empty())
discoverStreamPurposes(getPdb(), StreamPurposes);
@@ -527,18 +544,18 @@ static void dumpSectionContrib(LinePrinter &P, const SectionContrib2 &SC,
Error DumpOutputStyle::dumpModules() {
printHeader(P, "Modules");
- AutoIndent Indent(P);
if (File.isObj()) {
- P.formatLine("Dumping modules is not supported for object files");
+ printStreamNotValidForObj();
return Error::success();
}
if (!getPdb().hasPDBDbiStream()) {
- P.formatLine("DBI Stream not present");
+ printStreamNotPresent("DBI");
return Error::success();
}
+ AutoIndent Indent(P);
ExitOnError Err("Unexpected error processing modules: ");
auto &Stream = Err(getPdb().getPDBDbiStream());
@@ -570,7 +587,12 @@ Error DumpOutputStyle::dumpModuleFiles() {
printHeader(P, "Files");
if (File.isObj()) {
- P.formatLine("Dumping files is not valid for object files");
+ printStreamNotValidForObj();
+ return Error::success();
+ }
+
+ if (!getPdb().hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
return Error::success();
}
@@ -591,6 +613,11 @@ Error DumpOutputStyle::dumpModuleFiles() {
Error DumpOutputStyle::dumpSymbolStats() {
printHeader(P, "Module Stats");
+ if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
+ return Error::success();
+ }
+
ExitOnError Err("Unexpected error processing modules: ");
StatCollection SymStats;
@@ -625,9 +652,9 @@ Error DumpOutputStyle::dumpSymbolStats() {
}
});
- P.printLine(" Summary |");
- AutoIndent Indent(P, 4);
if (SymStats.Totals.Count > 0) {
+ P.printLine(" Summary |");
+ AutoIndent Indent(P, 4);
printModuleDetailStats<SymbolKind>(P, "Symbols", SymStats);
printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", ChunkStats);
}
@@ -680,6 +707,11 @@ static uint32_t getLongestTypeLeafName(const StatCollection &Stats) {
Error DumpOutputStyle::dumpUdtStats() {
printHeader(P, "S_UDT Record Stats");
+ if (File.isPdb() && !getPdb().hasPDBGlobalsStream()) {
+ printStreamNotPresent("Globals");
+ return Error::success();
+ }
+
StatCollection UdtStats;
StatCollection UdtTargetStats;
AutoIndent Indent(P, 4);
@@ -726,11 +758,6 @@ Error DumpOutputStyle::dumpUdtStats() {
P.NewLine();
if (File.isPdb()) {
- if (!getPdb().hasPDBGlobalsStream()) {
- P.printLine("- Error: globals stream not present");
- return Error::success();
- }
-
auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream());
auto ExpGlobals = getPdb().getPDBGlobalsStream();
if (!ExpGlobals)
@@ -839,6 +866,11 @@ static void typesetLinesAndColumns(LinePrinter &P, uint32_t Start,
Error DumpOutputStyle::dumpLines() {
printHeader(P, "Lines");
+ if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
+ return Error::success();
+ }
+
uint32_t LastModi = UINT32_MAX;
uint32_t LastNameIndex = UINT32_MAX;
iterateModuleSubsections<DebugLinesSubsectionRef>(
@@ -875,6 +907,11 @@ Error DumpOutputStyle::dumpLines() {
Error DumpOutputStyle::dumpInlineeLines() {
printHeader(P, "Inlinee Lines");
+ if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
+ return Error::success();
+ }
+
iterateModuleSubsections<DebugInlineeLinesSubsectionRef>(
File, PrintScope{P, 2},
[this](uint32_t Modi, const SymbolGroup &Strings,
@@ -893,6 +930,12 @@ Error DumpOutputStyle::dumpInlineeLines() {
Error DumpOutputStyle::dumpXmi() {
printHeader(P, "Cross Module Imports");
+
+ if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
+ return Error::success();
+ }
+
iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>(
File, PrintScope{P, 2},
[this](uint32_t Modi, const SymbolGroup &Strings,
@@ -929,6 +972,11 @@ Error DumpOutputStyle::dumpXmi() {
Error DumpOutputStyle::dumpXme() {
printHeader(P, "Cross Module Exports");
+ if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
+ return Error::success();
+ }
+
iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>(
File, PrintScope{P, 2},
[this](uint32_t Modi, const SymbolGroup &Strings,
@@ -943,6 +991,111 @@ Error DumpOutputStyle::dumpXme() {
return Error::success();
}
+std::string formatFrameType(object::frame_type FT) {
+ switch (FT) {
+ case object::frame_type::Fpo:
+ return "FPO";
+ case object::frame_type::NonFpo:
+ return "Non-FPO";
+ case object::frame_type::Trap:
+ return "Trap";
+ case object::frame_type::Tss:
+ return "TSS";
+ }
+ return "<unknown>";
+}
+
+Error DumpOutputStyle::dumpOldFpo(PDBFile &File) {
+ printHeader(P, "Old FPO Data");
+
+ ExitOnError Err("Error dumping old fpo data:");
+ auto &Dbi = Err(File.getPDBDbiStream());
+
+ uint32_t Index = Dbi.getDebugStreamIndex(DbgHeaderType::FPO);
+ if (Index == kInvalidStreamIndex) {
+ printStreamNotPresent("FPO");
+ return Error::success();
+ }
+
+ std::unique_ptr<MappedBlockStream> OldFpo = File.createIndexedStream(Index);
+ BinaryStreamReader Reader(*OldFpo);
+ FixedStreamArray<object::FpoData> Records;
+ Err(Reader.readArray(Records,
+ Reader.bytesRemaining() / sizeof(object::FpoData)));
+
+ P.printLine(" RVA | Code | Locals | Params | Prolog | Saved Regs | Use "
+ "BP | Has SEH | Frame Type");
+
+ for (const object::FpoData &FD : Records) {
+ P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,6} | {5,10} | {6,6} | "
+ "{7,7} | {8,9}",
+ uint32_t(FD.Offset), uint32_t(FD.Size), uint32_t(FD.NumLocals),
+ uint32_t(FD.NumParams), FD.getPrologSize(),
+ FD.getNumSavedRegs(), FD.useBP(), FD.hasSEH(),
+ formatFrameType(FD.getFP()));
+ }
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpNewFpo(PDBFile &File) {
+ printHeader(P, "New FPO Data");
+
+ ExitOnError Err("Error dumping new fpo data:");
+ auto &Dbi = Err(File.getPDBDbiStream());
+
+ uint32_t Index = Dbi.getDebugStreamIndex(DbgHeaderType::NewFPO);
+ if (Index == kInvalidStreamIndex) {
+ printStreamNotPresent("New FPO");
+ return Error::success();
+ }
+
+ std::unique_ptr<MappedBlockStream> NewFpo = File.createIndexedStream(Index);
+
+ DebugFrameDataSubsectionRef FDS;
+ if (auto EC = FDS.initialize(*NewFpo))
+ return make_error<RawError>(raw_error_code::corrupt_file,
+ "Invalid new fpo stream");
+
+ P.printLine(" RVA | Code | Locals | Params | Stack | Prolog | Saved Regs "
+ "| Has SEH | Has C++EH | Start | Program");
+ for (const FrameData &FD : FDS) {
+ bool IsFuncStart = FD.Flags & FrameData::IsFunctionStart;
+ bool HasEH = FD.Flags & FrameData::HasEH;
+ bool HasSEH = FD.Flags & FrameData::HasSEH;
+
+ auto &StringTable = Err(File.getStringTable());
+
+ auto Program = Err(StringTable.getStringForID(FD.FrameFunc));
+ P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,5} | {5,6} | {6,10} | "
+ "{7,7} | {8,9} | {9,5} | {10}",
+ uint32_t(FD.RvaStart), uint32_t(FD.CodeSize),
+ uint32_t(FD.LocalSize), uint32_t(FD.ParamsSize),
+ uint32_t(FD.MaxStackSize), uint16_t(FD.PrologSize),
+ uint16_t(FD.SavedRegsSize), HasSEH, HasEH, IsFuncStart,
+ Program);
+ }
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpFpo() {
+ if (!File.isPdb()) {
+ printStreamNotValidForObj();
+ return Error::success();
+ }
+
+ PDBFile &File = getPdb();
+ if (!File.hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
+ return Error::success();
+ }
+
+ if (auto EC = dumpOldFpo(File))
+ return EC;
+ if (auto EC = dumpNewFpo(File))
+ return EC;
+ return Error::success();
+}
+
Error DumpOutputStyle::dumpStringTableFromPdb() {
AutoIndent Indent(P);
auto IS = getPdb().getStringTable();
@@ -965,7 +1118,7 @@ Error DumpOutputStyle::dumpStringTableFromPdb() {
std::vector<uint32_t> SortedIDs(IS->name_ids().begin(),
IS->name_ids().end());
- llvm::sort(SortedIDs.begin(), SortedIDs.end());
+ llvm::sort(SortedIDs);
for (uint32_t I : SortedIDs) {
auto ES = IS->getStringForID(I);
llvm::SmallString<32> Str;
@@ -1037,12 +1190,13 @@ Error DumpOutputStyle::dumpStringTableFromObj() {
Error DumpOutputStyle::dumpNamedStreams() {
printHeader(P, "Named Streams");
- AutoIndent Indent(P, 2);
if (File.isObj()) {
- P.formatLine("Dumping Named Streams is only supported for PDB files.");
+ printStreamNotValidForObj();
return Error::success();
}
+
+ AutoIndent Indent(P);
ExitOnError Err("Invalid PDB File: ");
auto &IS = Err(File.pdb().getPDBInfoStream());
@@ -1087,13 +1241,13 @@ static void
dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types,
uint32_t NumTypeRecords, uint32_t NumHashBuckets,
FixedStreamArray<support::ulittle32_t> HashValues,
- bool Bytes, bool Extras) {
+ TpiStream *Stream, bool Bytes, bool Extras) {
Printer.formatLine("Showing {0:N} records", NumTypeRecords);
uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords);
MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types,
- NumHashBuckets, HashValues);
+ NumHashBuckets, HashValues, Stream);
if (auto EC = codeview::visitTypeStream(Types, V)) {
Printer.formatLine("An error occurred dumping type records: {0}",
@@ -1109,7 +1263,8 @@ static void dumpPartialTypeStream(LinePrinter &Printer,
NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());
MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types,
- Stream.getNumHashBuckets(), Stream.getHashValues());
+ Stream.getNumHashBuckets(), Stream.getHashValues(),
+ &Stream);
if (opts::dump::DumpTypeDependents) {
// If we need to dump all dependents, then iterate each index and find
@@ -1171,7 +1326,8 @@ Error DumpOutputStyle::dumpTypesFromObjectFile() {
Types.reset(Reader, 100);
if (opts::dump::DumpTypes) {
- dumpFullTypeStream(P, Types, 0, 0, {}, opts::dump::DumpTypeData, false);
+ dumpFullTypeStream(P, Types, 0, 0, {}, nullptr, opts::dump::DumpTypeData,
+ false);
} else if (opts::dump::DumpTypeExtras) {
auto LocalHashes = LocallyHashedType::hashTypeCollection(Types);
auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types);
@@ -1204,7 +1360,6 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
printHeader(P, "Types (IPI Stream)");
}
- AutoIndent Indent(P);
assert(!File.isObj());
bool Present = false;
@@ -1229,10 +1384,11 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
}
if (!Present) {
- P.formatLine("Stream not present");
+ printStreamNotPresent(StreamIdx == StreamTPI ? "TPI" : "IPI");
return Error::success();
}
+ AutoIndent Indent(P);
ExitOnError Err("Unexpected error processing types: ");
auto &Stream = Err((StreamIdx == StreamTPI) ? getPdb().getPDBTpiStream()
@@ -1240,11 +1396,14 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids();
+ // Enable resolving forward decls.
+ Stream.buildHashMap();
+
if (DumpTypes || !Indices.empty()) {
if (Indices.empty())
dumpFullTypeStream(P, Types, Stream.getNumTypeRecords(),
Stream.getNumHashBuckets(), Stream.getHashValues(),
- DumpBytes, DumpExtras);
+ &Stream, DumpBytes, DumpExtras);
else {
std::vector<TypeIndex> TiList(Indices.begin(), Indices.end());
dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras,
@@ -1261,19 +1420,21 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset));
}
- P.NewLine();
- P.formatLine("Hash Adjusters:");
- auto &Adjusters = Stream.getHashAdjusters();
- auto &Strings = Err(getPdb().getStringTable());
- for (const auto &A : Adjusters) {
- AutoIndent Indent2(P);
- auto ExpectedStr = Strings.getStringForID(A.first);
- TypeIndex TI(A.second);
- if (ExpectedStr)
- P.formatLine("`{0}` -> {1}", *ExpectedStr, TI);
- else {
- P.formatLine("unknown str id ({0}) -> {1}", A.first, TI);
- consumeError(ExpectedStr.takeError());
+ if (getPdb().hasPDBStringTable()) {
+ P.NewLine();
+ P.formatLine("Hash Adjusters:");
+ auto &Adjusters = Stream.getHashAdjusters();
+ auto &Strings = Err(getPdb().getStringTable());
+ for (const auto &A : Adjusters) {
+ AutoIndent Indent2(P);
+ auto ExpectedStr = Strings.getStringForID(A.first);
+ TypeIndex TI(A.second);
+ if (ExpectedStr)
+ P.formatLine("`{0}` -> {1}", *ExpectedStr, TI);
+ else {
+ P.formatLine("unknown str id ({0}) -> {1}", A.first, TI);
+ consumeError(ExpectedStr.takeError());
+ }
}
}
}
@@ -1321,12 +1482,12 @@ Error DumpOutputStyle::dumpModuleSymsForObj() {
Error DumpOutputStyle::dumpModuleSymsForPdb() {
printHeader(P, "Symbols");
- AutoIndent Indent(P);
- if (!getPdb().hasPDBDbiStream()) {
- P.formatLine("DBI Stream not present");
+ if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
return Error::success();
}
+ AutoIndent Indent(P);
ExitOnError Err("Unexpected error processing symbols: ");
auto &Ids = File.ids();
@@ -1364,18 +1525,19 @@ Error DumpOutputStyle::dumpModuleSymsForPdb() {
Error DumpOutputStyle::dumpGSIRecords() {
printHeader(P, "GSI Records");
- AutoIndent Indent(P);
if (File.isObj()) {
- P.formatLine("Dumping Globals is not supported for object files");
+ printStreamNotValidForObj();
return Error::success();
}
if (!getPdb().hasPDBSymbolStream()) {
- P.formatLine("GSI Common Symbol Stream not present");
+ printStreamNotPresent("GSI Common Symbol");
return Error::success();
}
+ AutoIndent Indent(P);
+
auto &Records = cantFail(getPdb().getPDBSymbolStream());
auto &Types = File.types();
auto &Ids = File.ids();
@@ -1397,38 +1559,72 @@ Error DumpOutputStyle::dumpGSIRecords() {
Error DumpOutputStyle::dumpGlobals() {
printHeader(P, "Global Symbols");
- AutoIndent Indent(P);
if (File.isObj()) {
- P.formatLine("Dumping Globals is not supported for object files");
+ printStreamNotValidForObj();
return Error::success();
}
if (!getPdb().hasPDBGlobalsStream()) {
- P.formatLine("Globals stream not present");
+ printStreamNotPresent("Globals");
return Error::success();
}
+
+ AutoIndent Indent(P);
ExitOnError Err("Error dumping globals stream: ");
auto &Globals = Err(getPdb().getPDBGlobalsStream());
- const GSIHashTable &Table = Globals.getGlobalsTable();
- Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras));
+ if (opts::dump::DumpGlobalNames.empty()) {
+ const GSIHashTable &Table = Globals.getGlobalsTable();
+ Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras));
+ } else {
+ SymbolStream &SymRecords = cantFail(getPdb().getPDBSymbolStream());
+ auto &Types = File.types();
+ auto &Ids = File.ids();
+
+ SymbolVisitorCallbackPipeline Pipeline;
+ SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
+ MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
+
+ Pipeline.addCallbackToPipeline(Deserializer);
+ Pipeline.addCallbackToPipeline(Dumper);
+ CVSymbolVisitor Visitor(Pipeline);
+
+ using ResultEntryType = std::pair<uint32_t, CVSymbol>;
+ for (StringRef Name : opts::dump::DumpGlobalNames) {
+ AutoIndent Indent(P);
+ P.formatLine("Global Name `{0}`", Name);
+ std::vector<ResultEntryType> Results =
+ Globals.findRecordsByName(Name, SymRecords);
+ if (Results.empty()) {
+ AutoIndent Indent(P);
+ P.printLine("(no matching records found)");
+ continue;
+ }
+
+ for (ResultEntryType Result : Results) {
+ if (auto E = Visitor.visitSymbolRecord(Result.second, Result.first))
+ return E;
+ }
+ }
+ }
return Error::success();
}
Error DumpOutputStyle::dumpPublics() {
printHeader(P, "Public Symbols");
- AutoIndent Indent(P);
if (File.isObj()) {
- P.formatLine("Dumping Globals is not supported for object files");
+ printStreamNotValidForObj();
return Error::success();
}
if (!getPdb().hasPDBPublicsStream()) {
- P.formatLine("Publics stream not present");
+ printStreamNotPresent("Publics");
return Error::success();
}
+
+ AutoIndent Indent(P);
ExitOnError Err("Error dumping publics stream: ");
auto &Publics = Err(getPdb().getPDBPublicsStream());
@@ -1514,8 +1710,6 @@ Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table,
// Return early if we aren't dumping public hash table and address map info.
if (HashExtras) {
- P.formatBinary("Hash Bitmap", Table.HashBitmap, 0);
-
P.formatLine("Hash Entries");
{
AutoIndent Indent2(P);
@@ -1560,12 +1754,17 @@ Error DumpOutputStyle::dumpSectionHeaders() {
void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) {
printHeader(P, Label);
- AutoIndent Indent(P);
if (File.isObj()) {
- P.formatLine("Dumping Section Headers is not supported for object files");
+ printStreamNotValidForObj();
+ return;
+ }
+
+ if (!getPdb().hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
return;
}
+ AutoIndent Indent(P);
ExitOnError Err("Error dumping section headers: ");
std::unique_ptr<MappedBlockStream> Stream;
ArrayRef<object::coff_section> Headers;
@@ -1606,20 +1805,19 @@ void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) {
Error DumpOutputStyle::dumpSectionContribs() {
printHeader(P, "Section Contributions");
- AutoIndent Indent(P);
if (File.isObj()) {
- P.formatLine(
- "Dumping section contributions is not supported for object files");
+ printStreamNotValidForObj();
return Error::success();
}
- ExitOnError Err("Error dumping section contributions: ");
if (!getPdb().hasPDBDbiStream()) {
- P.formatLine(
- "Section contribs require a DBI Stream, which could not be loaded");
+ printStreamNotPresent("DBI");
return Error::success();
}
+ AutoIndent Indent(P);
+ ExitOnError Err("Error dumping section contributions: ");
+
auto &Dbi = Err(getPdb().getPDBDbiStream());
class Visitor : public ISectionContribVisitor {
@@ -1651,21 +1849,20 @@ Error DumpOutputStyle::dumpSectionContribs() {
Error DumpOutputStyle::dumpSectionMap() {
printHeader(P, "Section Map");
- AutoIndent Indent(P);
if (File.isObj()) {
- P.formatLine("Dumping section map is not supported for object files");
+ printStreamNotValidForObj();
return Error::success();
}
- ExitOnError Err("Error dumping section map: ");
-
if (!getPdb().hasPDBDbiStream()) {
- P.formatLine("Dumping the section map requires a DBI Stream, which could "
- "not be loaded");
+ printStreamNotPresent("DBI");
return Error::success();
}
+ AutoIndent Indent(P);
+ ExitOnError Err("Error dumping section map: ");
+
auto &Dbi = Err(getPdb().getPDBDbiStream());
uint32_t I = 0;
diff --git a/tools/llvm-pdbutil/DumpOutputStyle.h b/tools/llvm-pdbutil/DumpOutputStyle.h
index e7e9252f2fa9..9b3a85587bde 100644
--- a/tools/llvm-pdbutil/DumpOutputStyle.h
+++ b/tools/llvm-pdbutil/DumpOutputStyle.h
@@ -70,6 +70,9 @@ private:
PDBFile &getPdb();
object::COFFObjectFile &getObj();
+ void printStreamNotValidForObj();
+ void printStreamNotPresent(StringRef StreamName);
+
Error dumpFileSummary();
Error dumpStreamSummary();
Error dumpSymbolStats();
@@ -82,6 +85,9 @@ private:
Error dumpInlineeLines();
Error dumpXmi();
Error dumpXme();
+ Error dumpFpo();
+ Error dumpOldFpo(PDBFile &File);
+ Error dumpNewFpo(PDBFile &File);
Error dumpTpiStream(uint32_t StreamIdx);
Error dumpTypesFromObjectFile();
Error dumpModules();
diff --git a/tools/llvm-pdbutil/InputFile.cpp b/tools/llvm-pdbutil/InputFile.cpp
index 7b5af7e96920..8eb116cf0d80 100644
--- a/tools/llvm-pdbutil/InputFile.cpp
+++ b/tools/llvm-pdbutil/InputFile.cpp
@@ -41,6 +41,10 @@ getModuleDebugStream(PDBFile &File, StringRef &ModuleName, uint32_t Index) {
auto &Dbi = Err(File.getPDBDbiStream());
const auto &Modules = Dbi.modules();
+ if (Index >= Modules.getModuleCount())
+ return make_error<RawError>(raw_error_code::index_out_of_bounds,
+ "Invalid module index");
+
auto Modi = Modules.getModuleDescriptor(Index);
ModuleName = Modi.getModuleName();
@@ -112,10 +116,6 @@ static std::string formatChecksumKind(FileChecksumKind Kind) {
return formatUnknownEnum(Kind);
}
-static const DebugStringTableSubsectionRef &extractStringTable(PDBFile &File) {
- return cantFail(File.getStringTable()).getStringTable();
-}
-
template <typename... Args>
static void formatInternal(LinePrinter &Printer, bool Append, Args &&... args) {
if (Append)
@@ -164,8 +164,13 @@ void SymbolGroup::initializeForPdb(uint32_t Modi) {
// PDB always uses the same string table, but each module has its own
// checksums. So we only set the strings if they're not already set.
- if (!SC.hasStrings())
- SC.setStrings(extractStringTable(File->pdb()));
+ if (!SC.hasStrings()) {
+ auto StringTable = File->pdb().getStringTable();
+ if (StringTable)
+ SC.setStrings(StringTable->getStringTable());
+ else
+ consumeError(StringTable.takeError());
+ }
SC.resetChecksums();
auto MDS = getModuleDebugStream(File->pdb(), Name, Modi);
diff --git a/tools/llvm-pdbutil/InputFile.h b/tools/llvm-pdbutil/InputFile.h
index 552f3a3b2127..ee4e651c1e99 100644
--- a/tools/llvm-pdbutil/InputFile.h
+++ b/tools/llvm-pdbutil/InputFile.h
@@ -110,6 +110,8 @@ public:
const InputFile &getFile() const { return *File; }
InputFile &getFile() { return *File; }
+ bool hasDebugStream() const { return DebugStream != nullptr; }
+
private:
void initializeForPdb(uint32_t Modi);
void updatePdbModi(uint32_t Modi);
diff --git a/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
index f4e38a32a511..2c7b213b0a9f 100644
--- a/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
+++ b/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
@@ -296,6 +296,14 @@ static std::string formatRegisterId(RegisterId Id) {
return formatUnknownEnum(Id);
}
+static std::string formatRegisterId(uint16_t Reg16) {
+ return formatRegisterId(RegisterId(Reg16));
+}
+
+static std::string formatRegisterId(ulittle16_t &Reg16) {
+ return formatRegisterId(uint16_t(Reg16));
+}
+
static std::string formatRange(LocalVariableAddrRange Range) {
return formatv("[{0},+{1})",
formatSegmentOffset(Range.ISectStart, Range.OffsetStart),
@@ -482,6 +490,7 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
AutoIndent Indent(P, 7);
SourceLanguage Lang = static_cast<SourceLanguage>(
Compile2.Flags & CompileSym2Flags::SourceLanguageMask);
+ CompilationCPU = Compile2.Machine;
P.formatLine("machine = {0}, ver = {1}, language = {2}",
formatMachineType(Compile2.Machine), Compile2.Version,
formatSourceLanguage(Lang));
@@ -502,6 +511,7 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
AutoIndent Indent(P, 7);
SourceLanguage Lang = static_cast<SourceLanguage>(
Compile3.Flags & CompileSym3Flags::SourceLanguageMask);
+ CompilationCPU = Compile3.Machine;
P.formatLine("machine = {0}, Ver = {1}, language = {2}",
formatMachineType(Compile3.Machine), Compile3.Version,
formatSourceLanguage(Lang));
@@ -550,10 +560,11 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
DefRangeRegisterRelSym &Def) {
AutoIndent Indent(P, 7);
- P.formatLine("register = {0}, base ptr = {1}, offset in parent = {2}, has "
+ P.formatLine("register = {0}, offset = {1}, offset in parent = {2}, has "
"spilled udt = {3}",
- uint16_t(Def.Hdr.Register), int32_t(Def.Hdr.BasePointerOffset),
- Def.offsetInParent(), Def.hasSpilledUDTMember());
+ formatRegisterId(Def.Hdr.Register),
+ int32_t(Def.Hdr.BasePointerOffset), Def.offsetInParent(),
+ Def.hasSpilledUDTMember());
P.formatLine("range = {0}, gaps = {1}", formatRange(Def.Range),
formatGaps(P.getIndentLevel() + 9, Def.Gaps));
return Error::success();
@@ -564,8 +575,8 @@ Error MinimalSymbolDumper::visitKnownRecord(
AutoIndent Indent(P, 7);
P.formatLine("register = {0}, may have no name = {1}, range start = "
"{2}, length = {3}",
- uint16_t(DefRangeRegister.Hdr.Register),
- uint16_t(DefRangeRegister.Hdr.MayHaveNoName),
+ formatRegisterId(DefRangeRegister.Hdr.Register),
+ bool(DefRangeRegister.Hdr.MayHaveNoName),
formatSegmentOffset(DefRangeRegister.Range.ISectStart,
DefRangeRegister.Range.OffsetStart),
DefRangeRegister.Range.Range);
@@ -579,7 +590,7 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
AutoIndent Indent(P, 7);
bool NoName = !!(Def.Hdr.MayHaveNoName == 0);
P.formatLine("register = {0}, may have no name = {1}, offset in parent = {2}",
- uint16_t(Def.Hdr.Register), NoName,
+ formatRegisterId(Def.Hdr.Register), NoName,
uint32_t(Def.Hdr.OffsetInParent));
P.formatLine("range = {0}, gaps = {1}", formatRange(Def.Range),
formatGaps(P.getIndentLevel() + 9, Def.Gaps));
@@ -606,8 +617,8 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, DefRangeSym &Def) {
Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FrameCookieSym &FC) {
AutoIndent Indent(P, 7);
P.formatLine("code offset = {0}, Register = {1}, kind = {2}, flags = {3}",
- FC.CodeOffset, FC.Register, formatCookieKind(FC.CookieKind),
- FC.Flags);
+ FC.CodeOffset, formatRegisterId(FC.Register),
+ formatCookieKind(FC.CookieKind), FC.Flags);
return Error::success();
}
@@ -620,6 +631,9 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FrameProcSym &FP) {
FP.BytesOfCalleeSavedRegisters,
formatSegmentOffset(FP.SectionIdOfExceptionHandler,
FP.OffsetOfExceptionHandler));
+ P.formatLine("local fp reg = {0}, param fp reg = {1}",
+ formatRegisterId(FP.getLocalFramePtrReg(CompilationCPU)),
+ formatRegisterId(FP.getParamFramePtrReg(CompilationCPU)));
P.formatLine("flags = {0}",
formatFrameProcedureOptions(P.getIndentLevel() + 9, FP.Flags));
return Error::success();
diff --git a/tools/llvm-pdbutil/MinimalSymbolDumper.h b/tools/llvm-pdbutil/MinimalSymbolDumper.h
index 1c26a85a4eaf..033e193cee6c 100644
--- a/tools/llvm-pdbutil/MinimalSymbolDumper.h
+++ b/tools/llvm-pdbutil/MinimalSymbolDumper.h
@@ -53,6 +53,11 @@ private:
std::string idIndex(codeview::TypeIndex TI) const;
LinePrinter &P;
+
+ /// Dumping certain records requires knowing what machine this is. The
+ /// S_COMPILE3 record will tell us, but if we don't see one, default to X64.
+ codeview::CPUType CompilationCPU = codeview::CPUType::X64;
+
bool RecordBytes;
const SymbolGroup *SymGroup = nullptr;
codeview::LazyRandomTypeCollection &Ids;
@@ -61,4 +66,4 @@ private:
} // namespace pdb
} // namespace llvm
-#endif \ No newline at end of file
+#endif
diff --git a/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/tools/llvm-pdbutil/MinimalTypeDumper.cpp
index 569bca7490fa..3f10e8ab8a1e 100644
--- a/tools/llvm-pdbutil/MinimalTypeDumper.cpp
+++ b/tools/llvm-pdbutil/MinimalTypeDumper.cpp
@@ -12,6 +12,7 @@
#include "FormatUtil.h"
#include "LinePrinter.h"
+#include "llvm-pdbutil.h"
#include "llvm/DebugInfo/CodeView/CVRecord.h"
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/CodeView.h"
@@ -19,6 +20,7 @@
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MathExtras.h"
@@ -27,15 +29,37 @@ using namespace llvm::codeview;
using namespace llvm::pdb;
static std::string formatClassOptions(uint32_t IndentLevel,
- ClassOptions Options) {
+ ClassOptions Options, TpiStream *Stream,
+ TypeIndex CurrentTypeIndex) {
std::vector<std::string> Opts;
+
+ if (Stream && Stream->supportsTypeLookup() &&
+ !opts::dump::DontResolveForwardRefs &&
+ ((Options & ClassOptions::ForwardReference) != ClassOptions::None)) {
+ // If we're able to resolve forward references, do that.
+ Expected<TypeIndex> ETI =
+ Stream->findFullDeclForForwardRef(CurrentTypeIndex);
+ if (!ETI) {
+ consumeError(ETI.takeError());
+ PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref (??\?)");
+ } else {
+ const char *Direction = (*ETI == CurrentTypeIndex)
+ ? "="
+ : ((*ETI < CurrentTypeIndex) ? "<-" : "->");
+ std::string Formatted =
+ formatv("forward ref ({0} {1})", Direction, *ETI).str();
+ PUSH_FLAG(ClassOptions, ForwardReference, Options, std::move(Formatted));
+ }
+ } else {
+ PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref");
+ }
+
PUSH_FLAG(ClassOptions, HasConstructorOrDestructor, Options,
"has ctor / dtor");
PUSH_FLAG(ClassOptions, ContainsNestedClass, Options,
"contains nested class");
PUSH_FLAG(ClassOptions, HasConversionOperator, Options,
"conversion operator");
- PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref");
PUSH_FLAG(ClassOptions, HasUniqueName, Options, "has unique name");
PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin");
PUSH_FLAG(ClassOptions, Nested, Options, "is nested");
@@ -194,6 +218,7 @@ static std::string formatFunctionOptions(FunctionOptions Options) {
}
Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) {
+ CurrentTypeIndex = Index;
// formatLine puts the newline at the beginning, so we use formatLine here
// to start a new line, and then individual visit methods use format to
// append to the existing line.
@@ -304,7 +329,8 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
P.formatLine("vtable: {0}, base list: {1}, field list: {2}",
Class.VTableShape, Class.DerivationList, Class.FieldList);
P.formatLine("options: {0}, sizeof {1}",
- formatClassOptions(P.getIndentLevel(), Class.Options),
+ formatClassOptions(P.getIndentLevel(), Class.Options, Stream,
+ CurrentTypeIndex),
Class.Size);
return Error::success();
}
@@ -316,7 +342,8 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
P.formatLine("unique name: `{0}`", Union.UniqueName);
P.formatLine("field list: {0}", Union.FieldList);
P.formatLine("options: {0}, sizeof {1}",
- formatClassOptions(P.getIndentLevel(), Union.Options),
+ formatClassOptions(P.getIndentLevel(), Union.Options, Stream,
+ CurrentTypeIndex),
Union.Size);
return Error::success();
}
@@ -328,7 +355,8 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList,
Enum.UnderlyingType);
P.formatLine("options: {0}",
- formatClassOptions(P.getIndentLevel(), Enum.Options));
+ formatClassOptions(P.getIndentLevel(), Enum.Options, Stream,
+ CurrentTypeIndex));
return Error::success();
}
diff --git a/tools/llvm-pdbutil/MinimalTypeDumper.h b/tools/llvm-pdbutil/MinimalTypeDumper.h
index 4227688f0f71..8f6bdc6110ae 100644
--- a/tools/llvm-pdbutil/MinimalTypeDumper.h
+++ b/tools/llvm-pdbutil/MinimalTypeDumper.h
@@ -20,15 +20,18 @@ class LazyRandomTypeCollection;
namespace pdb {
class LinePrinter;
+class TpiStream;
class MinimalTypeDumpVisitor : public codeview::TypeVisitorCallbacks {
public:
MinimalTypeDumpVisitor(LinePrinter &P, uint32_t Width, bool RecordBytes,
bool Hashes, codeview::LazyRandomTypeCollection &Types,
uint32_t NumHashBuckets,
- FixedStreamArray<support::ulittle32_t> HashValues)
+ FixedStreamArray<support::ulittle32_t> HashValues,
+ pdb::TpiStream *Stream)
: P(P), Width(Width), RecordBytes(RecordBytes), Hashes(Hashes),
- Types(Types), NumHashBuckets(NumHashBuckets), HashValues(HashValues) {}
+ Types(Types), NumHashBuckets(NumHashBuckets), HashValues(HashValues),
+ Stream(Stream) {}
Error visitTypeBegin(codeview::CVType &Record,
codeview::TypeIndex Index) override;
@@ -55,7 +58,9 @@ private:
bool Hashes = false;
codeview::LazyRandomTypeCollection &Types;
uint32_t NumHashBuckets;
+ codeview::TypeIndex CurrentTypeIndex;
FixedStreamArray<support::ulittle32_t> HashValues;
+ pdb::TpiStream *Stream = nullptr;
};
} // namespace pdb
} // namespace llvm
diff --git a/tools/llvm-pdbutil/PdbYaml.cpp b/tools/llvm-pdbutil/PdbYaml.cpp
index eb39708a27e9..3ea333608314 100644
--- a/tools/llvm-pdbutil/PdbYaml.cpp
+++ b/tools/llvm-pdbutil/PdbYaml.cpp
@@ -110,6 +110,7 @@ void MappingTraits<PdbObject>::mapping(IO &IO, PdbObject &Obj) {
IO.mapOptional("DbiStream", Obj.DbiStream);
IO.mapOptional("TpiStream", Obj.TpiStream);
IO.mapOptional("IpiStream", Obj.IpiStream);
+ IO.mapOptional("PublicsStream", Obj.PublicsStream);
}
void MappingTraits<MSFHeaders>::mapping(IO &IO, MSFHeaders &Obj) {
@@ -163,6 +164,11 @@ void MappingTraits<PdbTpiStream>::mapping(IO &IO,
IO.mapRequired("Records", Obj.Records);
}
+void MappingTraits<PdbPublicsStream>::mapping(
+ IO &IO, pdb::yaml::PdbPublicsStream &Obj) {
+ IO.mapRequired("Records", Obj.PubSyms);
+}
+
void MappingTraits<NamedStreamMapping>::mapping(IO &IO,
NamedStreamMapping &Obj) {
IO.mapRequired("Name", Obj.StreamName);
diff --git a/tools/llvm-pdbutil/PdbYaml.h b/tools/llvm-pdbutil/PdbYaml.h
index 91e054490a5f..97ba87266cc6 100644
--- a/tools/llvm-pdbutil/PdbYaml.h
+++ b/tools/llvm-pdbutil/PdbYaml.h
@@ -92,6 +92,10 @@ struct PdbTpiStream {
std::vector<CodeViewYAML::LeafRecord> Records;
};
+struct PdbPublicsStream {
+ std::vector<CodeViewYAML::SymbolRecord> PubSyms;
+};
+
struct PdbObject {
explicit PdbObject(BumpPtrAllocator &Allocator) : Allocator(Allocator) {}
@@ -102,6 +106,7 @@ struct PdbObject {
Optional<PdbDbiStream> DbiStream;
Optional<PdbTpiStream> TpiStream;
Optional<PdbTpiStream> IpiStream;
+ Optional<PdbPublicsStream> PublicsStream;
Optional<std::vector<StringRef>> StringTable;
@@ -118,6 +123,7 @@ LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::StreamBlockList)
LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbInfoStream)
LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbDbiStream)
LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbTpiStream)
+LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbPublicsStream)
LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::NamedStreamMapping)
LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbModiStream)
LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbDbiModuleInfo)
diff --git a/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp b/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp
index 651cb8b7649e..f009f53a3932 100644
--- a/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp
+++ b/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp
@@ -51,6 +51,13 @@ void ClassDefinitionDumper::prettyPrintClassIntro(const ClassLayout &Layout) {
uint32_t Size = Layout.getSize();
const PDBSymbolTypeUDT &Class = Layout.getClass();
+ if (Layout.getClass().isConstType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "const ";
+ if (Layout.getClass().isVolatileType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile ";
+ if (Layout.getClass().isUnalignedType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "unaligned ";
+
WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " ";
WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName();
WithColor(Printer, PDB_ColorItem::Comment).get() << " [sizeof = " << Size
diff --git a/tools/llvm-pdbutil/PrettyCompilandDumper.cpp b/tools/llvm-pdbutil/PrettyCompilandDumper.cpp
index 0d99c9b1245c..94a0b2d5e780 100644
--- a/tools/llvm-pdbutil/PrettyCompilandDumper.cpp
+++ b/tools/llvm-pdbutil/PrettyCompilandDumper.cpp
@@ -28,6 +28,7 @@
#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h"
#include "llvm/DebugInfo/PDB/PDBSymbolUnknown.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolUsingNamespace.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
@@ -216,3 +217,13 @@ void CompilandDumper::dump(const PDBSymbolUnknown &Symbol) {
Printer.NewLine();
Printer << "unknown (" << Symbol.getSymTag() << ")";
}
+
+void CompilandDumper::dump(const PDBSymbolUsingNamespace &Symbol) {
+ if (Printer.IsSymbolExcluded(Symbol.getName()))
+ return;
+
+ Printer.NewLine();
+ Printer << "using namespace ";
+ std::string Name = Symbol.getName();
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Name;
+}
diff --git a/tools/llvm-pdbutil/PrettyCompilandDumper.h b/tools/llvm-pdbutil/PrettyCompilandDumper.h
index cae196e9d134..1a840e49607c 100644
--- a/tools/llvm-pdbutil/PrettyCompilandDumper.h
+++ b/tools/llvm-pdbutil/PrettyCompilandDumper.h
@@ -34,6 +34,7 @@ public:
void dump(const PDBSymbolThunk &Symbol) override;
void dump(const PDBSymbolTypeTypedef &Symbol) override;
void dump(const PDBSymbolUnknown &Symbol) override;
+ void dump(const PDBSymbolUsingNamespace &Symbol) override;
private:
LinePrinter &Printer;
diff --git a/tools/llvm-pdbutil/PrettyEnumDumper.cpp b/tools/llvm-pdbutil/PrettyEnumDumper.cpp
index bf22e75e3949..f4cbd3f8fa14 100644
--- a/tools/llvm-pdbutil/PrettyEnumDumper.cpp
+++ b/tools/llvm-pdbutil/PrettyEnumDumper.cpp
@@ -23,6 +23,18 @@ using namespace llvm::pdb;
EnumDumper::EnumDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {}
void EnumDumper::start(const PDBSymbolTypeEnum &Symbol) {
+ if (Symbol.getUnmodifiedTypeId() != 0) {
+ if (Symbol.isConstType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "const ";
+ if (Symbol.isVolatileType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile ";
+ if (Symbol.isUnalignedType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "unaligned ";
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "enum ";
+ WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
+ return;
+ }
+
WithColor(Printer, PDB_ColorItem::Keyword).get() << "enum ";
WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
if (!opts::pretty::NoEnumDefs) {
diff --git a/tools/llvm-pdbutil/PrettyFunctionDumper.cpp b/tools/llvm-pdbutil/PrettyFunctionDumper.cpp
index 177d8a009a2b..836ede41054e 100644
--- a/tools/llvm-pdbutil/PrettyFunctionDumper.cpp
+++ b/tools/llvm-pdbutil/PrettyFunctionDumper.cpp
@@ -53,7 +53,10 @@ FunctionDumper::FunctionDumper(LinePrinter &P)
void FunctionDumper::start(const PDBSymbolTypeFunctionSig &Symbol,
const char *Name, PointerType Pointer) {
auto ReturnType = Symbol.getReturnType();
- ReturnType->dump(*this);
+ if (!ReturnType)
+ Printer << "<unknown-type>";
+ else
+ ReturnType->dump(*this);
Printer << " ";
uint32_t ClassParentId = Symbol.getClassParentId();
auto ClassParent =
@@ -225,9 +228,10 @@ void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) {
// through to the real thing and dump it.
uint32_t TypeId = Symbol.getTypeId();
auto Type = Symbol.getSession().getSymbolById(TypeId);
- if (!Type)
- return;
- Type->dump(*this);
+ if (Type)
+ Printer << "<unknown-type>";
+ else
+ Type->dump(*this);
}
void FunctionDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
diff --git a/tools/llvm-pdbutil/PrettyTypeDumper.cpp b/tools/llvm-pdbutil/PrettyTypeDumper.cpp
index 663a608fe429..daf3cd45b327 100644
--- a/tools/llvm-pdbutil/PrettyTypeDumper.cpp
+++ b/tools/llvm-pdbutil/PrettyTypeDumper.cpp
@@ -13,13 +13,17 @@
#include "PrettyBuiltinDumper.h"
#include "PrettyClassDefinitionDumper.h"
#include "PrettyEnumDumper.h"
+#include "PrettyFunctionDumper.h"
#include "PrettyTypedefDumper.h"
#include "llvm-pdbutil.h"
#include "llvm/DebugInfo/PDB/IPDBSession.h"
#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
#include "llvm/DebugInfo/PDB/UDTLayout.h"
@@ -128,36 +132,85 @@ filterAndSortClassDefs(LinePrinter &Printer, Enumerator &E,
}
if (Comp)
- llvm::sort(Filtered.begin(), Filtered.end(), Comp);
+ llvm::sort(Filtered, Comp);
return Filtered;
}
TypeDumper::TypeDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {}
-void TypeDumper::start(const PDBSymbolExe &Exe) {
- if (opts::pretty::Enums) {
- if (auto Enums = Exe.findAllChildren<PDBSymbolTypeEnum>()) {
+template <typename T>
+static bool isTypeExcluded(LinePrinter &Printer, const T &Symbol) {
+ return false;
+}
+
+static bool isTypeExcluded(LinePrinter &Printer,
+ const PDBSymbolTypeEnum &Enum) {
+ if (Printer.IsTypeExcluded(Enum.getName(), Enum.getLength()))
+ return true;
+ // Dump member enums when dumping their class definition.
+ if (nullptr != Enum.getClassParent())
+ return true;
+ return false;
+}
+
+static bool isTypeExcluded(LinePrinter &Printer,
+ const PDBSymbolTypeTypedef &Typedef) {
+ return Printer.IsTypeExcluded(Typedef.getName(), Typedef.getLength());
+}
+
+template <typename SymbolT>
+static void dumpSymbolCategory(LinePrinter &Printer, const PDBSymbolExe &Exe,
+ TypeDumper &TD, StringRef Label) {
+ if (auto Children = Exe.findAllChildren<SymbolT>()) {
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Label;
+ Printer << ": (" << Children->getChildCount() << " items)";
+ Printer.Indent();
+ while (auto Child = Children->getNext()) {
+ if (isTypeExcluded(Printer, *Child))
+ continue;
+
Printer.NewLine();
- WithColor(Printer, PDB_ColorItem::Identifier).get() << "Enums";
- Printer << ": (" << Enums->getChildCount() << " items)";
- Printer.Indent();
- while (auto Enum = Enums->getNext())
- Enum->dump(*this);
- Printer.Unindent();
+ Child->dump(TD);
}
+ Printer.Unindent();
}
+}
- if (opts::pretty::Typedefs) {
- if (auto Typedefs = Exe.findAllChildren<PDBSymbolTypeTypedef>()) {
- Printer.NewLine();
- WithColor(Printer, PDB_ColorItem::Identifier).get() << "Typedefs";
- Printer << ": (" << Typedefs->getChildCount() << " items)";
- Printer.Indent();
- while (auto Typedef = Typedefs->getNext())
- Typedef->dump(*this);
- Printer.Unindent();
- }
+static void printClassDecl(LinePrinter &Printer,
+ const PDBSymbolTypeUDT &Class) {
+ if (Class.getUnmodifiedTypeId() != 0) {
+ if (Class.isConstType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "const ";
+ if (Class.isVolatileType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile ";
+ if (Class.isUnalignedType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "unaligned ";
}
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " ";
+ WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName();
+}
+
+void TypeDumper::start(const PDBSymbolExe &Exe) {
+ if (opts::pretty::Enums)
+ dumpSymbolCategory<PDBSymbolTypeEnum>(Printer, Exe, *this, "Enums");
+
+ if (opts::pretty::Funcsigs)
+ dumpSymbolCategory<PDBSymbolTypeFunctionSig>(Printer, Exe, *this,
+ "Function Signatures");
+
+ if (opts::pretty::Typedefs)
+ dumpSymbolCategory<PDBSymbolTypeTypedef>(Printer, Exe, *this, "Typedefs");
+
+ if (opts::pretty::Arrays)
+ dumpSymbolCategory<PDBSymbolTypeArray>(Printer, Exe, *this, "Arrays");
+
+ if (opts::pretty::Pointers)
+ dumpSymbolCategory<PDBSymbolTypePointer>(Printer, Exe, *this, "Pointers");
+
+ if (opts::pretty::VTShapes)
+ dumpSymbolCategory<PDBSymbolTypeVTableShape>(Printer, Exe, *this,
+ "VFTable Shapes");
if (opts::pretty::Classes) {
if (auto Classes = Exe.findAllChildren<PDBSymbolTypeUDT>()) {
@@ -196,11 +249,16 @@ void TypeDumper::start(const PDBSymbolExe &Exe) {
dumpClassLayout(*Class);
} else {
while (auto Class = Classes->getNext()) {
- if (Class->getUnmodifiedTypeId() != 0)
+ if (Printer.IsTypeExcluded(Class->getName(), Class->getLength()))
continue;
- if (Printer.IsTypeExcluded(Class->getName(), Class->getLength()))
+ // No point duplicating a full class layout. Just print the modified
+ // declaration and continue.
+ if (Class->getUnmodifiedTypeId() != 0) {
+ Printer.NewLine();
+ printClassDecl(Printer, *Class);
continue;
+ }
auto Layout = llvm::make_unique<ClassLayout>(std::move(Class));
if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold)
@@ -218,35 +276,83 @@ void TypeDumper::start(const PDBSymbolExe &Exe) {
void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) {
assert(opts::pretty::Enums);
- if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength()))
- return;
- // Dump member enums when dumping their class definition.
- if (nullptr != Symbol.getClassParent())
- return;
-
- Printer.NewLine();
EnumDumper Dumper(Printer);
Dumper.start(Symbol);
}
+void TypeDumper::dump(const PDBSymbolTypeBuiltin &Symbol) {
+ BuiltinDumper BD(Printer);
+ BD.start(Symbol);
+}
+
+void TypeDumper::dump(const PDBSymbolTypeUDT &Symbol) {
+ printClassDecl(Printer, Symbol);
+}
+
void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
assert(opts::pretty::Typedefs);
- if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength()))
- return;
-
- Printer.NewLine();
TypedefDumper Dumper(Printer);
Dumper.start(Symbol);
}
+void TypeDumper::dump(const PDBSymbolTypeArray &Symbol) {
+ auto ElementType = Symbol.getElementType();
+
+ ElementType->dump(*this);
+ Printer << "[";
+ WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Symbol.getCount();
+ Printer << "]";
+}
+
+void TypeDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) {
+ FunctionDumper Dumper(Printer);
+ Dumper.start(Symbol, nullptr, FunctionDumper::PointerType::None);
+}
+
+void TypeDumper::dump(const PDBSymbolTypePointer &Symbol) {
+ std::unique_ptr<PDBSymbol> P = Symbol.getPointeeType();
+
+ if (auto *FS = dyn_cast<PDBSymbolTypeFunctionSig>(P.get())) {
+ FunctionDumper Dumper(Printer);
+ FunctionDumper::PointerType PT =
+ Symbol.isReference() ? FunctionDumper::PointerType::Reference
+ : FunctionDumper::PointerType::Pointer;
+ Dumper.start(*FS, nullptr, PT);
+ return;
+ }
+
+ if (auto *UDT = dyn_cast<PDBSymbolTypeUDT>(P.get())) {
+ printClassDecl(Printer, *UDT);
+ } else if (P) {
+ P->dump(*this);
+ }
+
+ if (auto Parent = Symbol.getClassParent()) {
+ auto UDT = llvm::unique_dyn_cast<PDBSymbolTypeUDT>(std::move(Parent));
+ if (UDT)
+ Printer << " " << UDT->getName() << "::";
+ }
+
+ if (Symbol.isReference())
+ Printer << "&";
+ else if (Symbol.isRValueReference())
+ Printer << "&&";
+ else
+ Printer << "*";
+}
+
+void TypeDumper::dump(const PDBSymbolTypeVTableShape &Symbol) {
+ Printer.format("<vtshape ({0} methods)>", Symbol.getCount());
+}
+
void TypeDumper::dumpClassLayout(const ClassLayout &Class) {
assert(opts::pretty::Classes);
if (opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::None) {
- Printer.NewLine();
- WithColor(Printer, PDB_ColorItem::Keyword).get() << "class ";
- WithColor(Printer, PDB_ColorItem::Identifier).get() << Class.getName();
+ WithColor(Printer, PDB_ColorItem::Keyword).get()
+ << Class.getClass().getUdtKind() << " ";
+ WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName();
} else {
ClassDefinitionDumper Dumper(Printer);
Dumper.start(Class);
diff --git a/tools/llvm-pdbutil/PrettyTypeDumper.h b/tools/llvm-pdbutil/PrettyTypeDumper.h
index 68a2f0246eba..36e586fea7e3 100644
--- a/tools/llvm-pdbutil/PrettyTypeDumper.h
+++ b/tools/llvm-pdbutil/PrettyTypeDumper.h
@@ -25,6 +25,12 @@ public:
void dump(const PDBSymbolTypeEnum &Symbol) override;
void dump(const PDBSymbolTypeTypedef &Symbol) override;
+ void dump(const PDBSymbolTypeFunctionSig &Symbol) override;
+ void dump(const PDBSymbolTypeArray &Symbol) override;
+ void dump(const PDBSymbolTypeBuiltin &Symbol) override;
+ void dump(const PDBSymbolTypePointer &Symbol) override;
+ void dump(const PDBSymbolTypeVTableShape &Symbol) override;
+ void dump(const PDBSymbolTypeUDT &Symbol) override;
void dumpClassLayout(const ClassLayout &Class);
diff --git a/tools/llvm-pdbutil/PrettyTypedefDumper.cpp b/tools/llvm-pdbutil/PrettyTypedefDumper.cpp
index 65443d6bca90..2b3f3691ed98 100644
--- a/tools/llvm-pdbutil/PrettyTypedefDumper.cpp
+++ b/tools/llvm-pdbutil/PrettyTypedefDumper.cpp
@@ -12,6 +12,7 @@
#include "LinePrinter.h"
#include "PrettyBuiltinDumper.h"
#include "PrettyFunctionDumper.h"
+#include "PrettyTypeDumper.h"
#include "llvm/DebugInfo/PDB/IPDBSession.h"
#include "llvm/DebugInfo/PDB/PDBExtras.h"
@@ -35,7 +36,10 @@ void TypedefDumper::start(const PDBSymbolTypeTypedef &Symbol) {
<< Symbol.getName();
}
-void TypedefDumper::dump(const PDBSymbolTypeArray &Symbol) {}
+void TypedefDumper::dump(const PDBSymbolTypeArray &Symbol) {
+ TypeDumper Dumper(Printer);
+ Dumper.dump(Symbol);
+}
void TypedefDumper::dump(const PDBSymbolTypeBuiltin &Symbol) {
BuiltinDumper Dumper(Printer);
diff --git a/tools/llvm-pdbutil/YAMLOutputStyle.cpp b/tools/llvm-pdbutil/YAMLOutputStyle.cpp
index a7afbf1242c5..62b5c428d410 100644
--- a/tools/llvm-pdbutil/YAMLOutputStyle.cpp
+++ b/tools/llvm-pdbutil/YAMLOutputStyle.cpp
@@ -18,10 +18,13 @@
#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h"
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
+#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
using namespace llvm;
@@ -68,6 +71,9 @@ Error YAMLOutputStyle::dump() {
if (auto EC = dumpIpiStream())
return EC;
+ if (auto EC = dumpPublics())
+ return EC;
+
flush();
return Error::success();
}
@@ -191,6 +197,9 @@ Error YAMLOutputStyle::dumpDbiStream() {
if (!opts::pdb2yaml::DbiStream)
return Error::success();
+ if (!File.hasPDBDbiStream())
+ return Error::success();
+
auto DbiS = File.getPDBDbiStream();
if (!DbiS)
return DbiS.takeError();
@@ -323,6 +332,42 @@ Error YAMLOutputStyle::dumpIpiStream() {
return Error::success();
}
+Error YAMLOutputStyle::dumpPublics() {
+ if (!opts::pdb2yaml::PublicsStream)
+ return Error::success();
+
+ Obj.PublicsStream.emplace();
+ auto ExpectedPublics = File.getPDBPublicsStream();
+ if (!ExpectedPublics) {
+ llvm::consumeError(ExpectedPublics.takeError());
+ return Error::success();
+ }
+
+ PublicsStream &Publics = *ExpectedPublics;
+ const GSIHashTable &PublicsTable = Publics.getPublicsTable();
+
+ auto ExpectedSyms = File.getPDBSymbolStream();
+ if (!ExpectedSyms) {
+ llvm::consumeError(ExpectedSyms.takeError());
+ return Error::success();
+ }
+
+ BinaryStreamRef SymStream =
+ ExpectedSyms->getSymbolArray().getUnderlyingStream();
+ for (uint32_t PubSymOff : PublicsTable) {
+ Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff);
+ if (!Sym)
+ return Sym.takeError();
+ auto ES = CodeViewYAML::SymbolRecord::fromCodeViewSymbol(*Sym);
+ if (!ES)
+ return ES.takeError();
+
+ Obj.PublicsStream->PubSyms.push_back(*ES);
+ }
+
+ return Error::success();
+}
+
void YAMLOutputStyle::flush() {
Out << Obj;
outs().flush();
diff --git a/tools/llvm-pdbutil/YAMLOutputStyle.h b/tools/llvm-pdbutil/YAMLOutputStyle.h
index 3690e3529d4a..a5ad3355d2ab 100644
--- a/tools/llvm-pdbutil/YAMLOutputStyle.h
+++ b/tools/llvm-pdbutil/YAMLOutputStyle.h
@@ -35,6 +35,7 @@ private:
Error dumpDbiStream();
Error dumpTpiStream();
Error dumpIpiStream();
+ Error dumpPublics();
void flush();
diff --git a/tools/llvm-pdbutil/llvm-pdbutil.cpp b/tools/llvm-pdbutil/llvm-pdbutil.cpp
index 5b0d21f83db7..76f61a2a95a7 100644
--- a/tools/llvm-pdbutil/llvm-pdbutil.cpp
+++ b/tools/llvm-pdbutil/llvm-pdbutil.cpp
@@ -13,7 +13,6 @@
#include "llvm-pdbutil.h"
-#include "Analyze.h"
#include "BytesOutputStyle.h"
#include "DumpOutputStyle.h"
#include "ExplainOutputStyle.h"
@@ -46,7 +45,6 @@
#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h"
#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
-#include "llvm/DebugInfo/PDB/GenericError.h"
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
#include "llvm/DebugInfo/PDB/IPDBInjectedSource.h"
#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h"
@@ -71,6 +69,8 @@
#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h"
#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
#include "llvm/Support/BinaryByteStream.h"
@@ -102,6 +102,9 @@ namespace opts {
cl::SubCommand DumpSubcommand("dump", "Dump MSF and CodeView debug info");
cl::SubCommand BytesSubcommand("bytes", "Dump raw bytes from the PDB file");
+cl::SubCommand DiaDumpSubcommand("diadump",
+ "Dump debug information using a DIA-like API");
+
cl::SubCommand
PrettySubcommand("pretty",
"Dump semantic information about types and symbols");
@@ -113,10 +116,6 @@ cl::SubCommand
PdbToYamlSubcommand("pdb2yaml",
"Generate a detailed YAML description of a PDB File");
-cl::SubCommand
- AnalyzeSubcommand("analyze",
- "Analyze various aspects of a PDB's structure");
-
cl::SubCommand MergeSubcommand("merge",
"Merge multiple PDBs into a single PDB");
@@ -155,6 +154,48 @@ cl::ValuesClass ChunkValues = cl::values(
"Any subsection not covered by another option"),
clEnumValN(ModuleSubsection::All, "all", "All known subsections"));
+namespace diadump {
+cl::list<std::string> InputFilenames(cl::Positional,
+ cl::desc("<input PDB files>"),
+ cl::OneOrMore, cl::sub(DiaDumpSubcommand));
+
+cl::opt<bool> Native("native", cl::desc("Use native PDB reader instead of DIA"),
+ cl::sub(DiaDumpSubcommand));
+
+static cl::opt<bool>
+ ShowClassHierarchy("hierarchy", cl::desc("Show lexical and class parents"),
+ cl::sub(DiaDumpSubcommand));
+static cl::opt<bool> NoSymIndexIds(
+ "no-ids",
+ cl::desc("Don't show any SymIndexId fields (overrides -hierarchy)"),
+ cl::sub(DiaDumpSubcommand));
+
+static cl::opt<bool>
+ Recurse("recurse",
+ cl::desc("When dumping a SymIndexId, dump the full details of the "
+ "corresponding record"),
+ cl::sub(DiaDumpSubcommand));
+
+static cl::opt<bool> Enums("enums", cl::desc("Dump enum types"),
+ cl::sub(DiaDumpSubcommand));
+static cl::opt<bool> Pointers("pointers", cl::desc("Dump enum types"),
+ cl::sub(DiaDumpSubcommand));
+static cl::opt<bool> UDTs("udts", cl::desc("Dump udt types"),
+ cl::sub(DiaDumpSubcommand));
+static cl::opt<bool> Compilands("compilands",
+ cl::desc("Dump compiland information"),
+ cl::sub(DiaDumpSubcommand));
+static cl::opt<bool> Funcsigs("funcsigs",
+ cl::desc("Dump function signature information"),
+ cl::sub(DiaDumpSubcommand));
+static cl::opt<bool> Arrays("arrays", cl::desc("Dump array types"),
+ cl::sub(DiaDumpSubcommand));
+static cl::opt<bool> VTShapes("vtshapes", cl::desc("Dump virtual table shapes"),
+ cl::sub(DiaDumpSubcommand));
+static cl::opt<bool> Typedefs("typedefs", cl::desc("Dump typedefs"),
+ cl::sub(DiaDumpSubcommand));
+} // namespace diadump
+
namespace pretty {
cl::list<std::string> InputFilenames(cl::Positional,
cl::desc("<input PDB files>"),
@@ -201,6 +242,15 @@ cl::opt<bool> Enums("enums", cl::desc("Display enum types"),
cl::cat(TypeCategory), cl::sub(PrettySubcommand));
cl::opt<bool> Typedefs("typedefs", cl::desc("Display typedef types"),
cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<bool> Funcsigs("funcsigs", cl::desc("Display function signatures"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<bool> Pointers("pointers", cl::desc("Display pointer types"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<bool> Arrays("arrays", cl::desc("Display arrays"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<bool> VTShapes("vtshapes", cl::desc("Display vftable shapes"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+
cl::opt<SymbolSortMode> SymbolOrder(
"symbol-order", cl::desc("symbol sort order"),
cl::init(SymbolSortMode::None),
@@ -432,6 +482,12 @@ cl::opt<bool> DumpTypeExtras("type-extras",
cl::desc("dump type hashes and index offsets"),
cl::cat(TypeOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DontResolveForwardRefs(
+ "dont-resolve-forward-refs",
+ cl::desc("When dumping type records for classes, unions, enums, and "
+ "structs, don't try to resolve forward references"),
+ cl::cat(TypeOptions), cl::sub(DumpSubcommand));
+
cl::list<uint32_t> DumpTypeIndex(
"type-index", cl::ZeroOrMore, cl::CommaSeparated,
cl::desc("only dump types with the specified hexadecimal type index"),
@@ -465,6 +521,11 @@ cl::opt<bool> DumpGlobals("globals", cl::desc("dump Globals symbol records"),
cl::cat(SymbolOptions), cl::sub(DumpSubcommand));
cl::opt<bool> DumpGlobalExtras("global-extras", cl::desc("dump Globals hashes"),
cl::cat(SymbolOptions), cl::sub(DumpSubcommand));
+cl::list<std::string> DumpGlobalNames(
+ "global-name",
+ cl::desc(
+ "With -globals, only dump globals whose name matches the given value"),
+ cl::cat(SymbolOptions), cl::sub(DumpSubcommand), cl::ZeroOrMore);
cl::opt<bool> DumpPublics("publics", cl::desc("dump Publics stream data"),
cl::cat(SymbolOptions), cl::sub(DumpSubcommand));
cl::opt<bool> DumpPublicExtras("public-extras",
@@ -482,6 +543,9 @@ cl::opt<bool>
cl::desc("dump CodeView symbol record raw bytes"),
cl::cat(SymbolOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpFpo("fpo", cl::desc("dump FPO records"),
+ cl::cat(SymbolOptions), cl::sub(DumpSubcommand));
+
// MODULE & FILE OPTIONS
cl::opt<bool> DumpModules("modules", cl::desc("dump compiland information"),
cl::cat(FileOptions), cl::sub(DumpSubcommand));
@@ -594,6 +658,10 @@ cl::opt<bool> IpiStream("ipi-stream",
cl::desc("Dump the IPI Stream (Stream 5)"),
cl::sub(PdbToYamlSubcommand), cl::init(false));
+cl::opt<bool> PublicsStream("publics-stream",
+ cl::desc("Dump the Publics Stream"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+
// MODULE & FILE OPTIONS
cl::opt<bool> DumpModules("modules", cl::desc("dump compiland information"),
cl::cat(FileOptions), cl::sub(PdbToYamlSubcommand));
@@ -613,14 +681,6 @@ cl::list<std::string> InputFilename(cl::Positional,
cl::sub(PdbToYamlSubcommand));
} // namespace pdb2yaml
-namespace analyze {
-cl::opt<bool> StringTable("hash-collisions", cl::desc("Find hash collisions"),
- cl::sub(AnalyzeSubcommand), cl::init(false));
-cl::list<std::string> InputFilename(cl::Positional,
- cl::desc("<input PDB file>"), cl::Required,
- cl::sub(AnalyzeSubcommand));
-}
-
namespace merge {
cl::list<std::string> InputFilenames(cl::Positional,
cl::desc("<input PDB files>"),
@@ -681,7 +741,7 @@ static void yamlToPdb(StringRef Path) {
/*RequiresNullTerminator=*/false);
if (ErrorOrBuffer.getError()) {
- ExitOnErr(make_error<GenericError>(generic_error_code::invalid_path, Path));
+ ExitOnErr(createFileError(Path, errorCodeToError(ErrorOrBuffer.getError())));
}
std::unique_ptr<MemoryBuffer> &Buffer = ErrorOrBuffer.get();
@@ -781,7 +841,8 @@ static void yamlToPdb(StringRef Path) {
Builder.getStringTableBuilder().setStrings(*Strings.strings());
- ExitOnErr(Builder.commit(opts::yaml2pdb::YamlPdbOutputFile));
+ codeview::GUID IgnoredOutGuid;
+ ExitOnErr(Builder.commit(opts::yaml2pdb::YamlPdbOutputFile, &IgnoredOutGuid));
}
static PDBFile &loadPDB(StringRef Path, std::unique_ptr<IPDBSession> &Session) {
@@ -817,14 +878,6 @@ static void dumpBytes(StringRef Path) {
ExitOnErr(O->dump());
}
-static void dumpAnalysis(StringRef Path) {
- std::unique_ptr<IPDBSession> Session;
- auto &File = loadPDB(Path, Session);
- auto O = llvm::make_unique<AnalysisStyle>(File);
-
- ExitOnErr(O->dump());
-}
-
bool opts::pretty::shouldDumpSymLevel(SymLevel Search) {
if (SymTypes.empty())
return true;
@@ -924,6 +977,69 @@ static void dumpInjectedSources(LinePrinter &Printer, IPDBSession &Session) {
}
}
+template <typename OuterT, typename ChildT>
+void diaDumpChildren(PDBSymbol &Outer, PdbSymbolIdField Ids,
+ PdbSymbolIdField Recurse) {
+ OuterT *ConcreteOuter = dyn_cast<OuterT>(&Outer);
+ if (!ConcreteOuter)
+ return;
+
+ auto Children = ConcreteOuter->template findAllChildren<ChildT>();
+ while (auto Child = Children->getNext()) {
+ outs() << " {";
+ Child->defaultDump(outs(), 4, Ids, Recurse);
+ outs() << "\n }\n";
+ }
+}
+
+static void dumpDia(StringRef Path) {
+ std::unique_ptr<IPDBSession> Session;
+
+ const auto ReaderType =
+ opts::diadump::Native ? PDB_ReaderType::Native : PDB_ReaderType::DIA;
+ ExitOnErr(loadDataForPDB(ReaderType, Path, Session));
+
+ auto GlobalScope = Session->getGlobalScope();
+
+ std::vector<PDB_SymType> SymTypes;
+
+ if (opts::diadump::Compilands)
+ SymTypes.push_back(PDB_SymType::Compiland);
+ if (opts::diadump::Enums)
+ SymTypes.push_back(PDB_SymType::Enum);
+ if (opts::diadump::Pointers)
+ SymTypes.push_back(PDB_SymType::PointerType);
+ if (opts::diadump::UDTs)
+ SymTypes.push_back(PDB_SymType::UDT);
+ if (opts::diadump::Funcsigs)
+ SymTypes.push_back(PDB_SymType::FunctionSig);
+ if (opts::diadump::Arrays)
+ SymTypes.push_back(PDB_SymType::ArrayType);
+ if (opts::diadump::VTShapes)
+ SymTypes.push_back(PDB_SymType::VTableShape);
+ if (opts::diadump::Typedefs)
+ SymTypes.push_back(PDB_SymType::Typedef);
+ PdbSymbolIdField Ids = opts::diadump::NoSymIndexIds ? PdbSymbolIdField::None
+ : PdbSymbolIdField::All;
+
+ PdbSymbolIdField Recurse = PdbSymbolIdField::None;
+ if (opts::diadump::Recurse)
+ Recurse = PdbSymbolIdField::All;
+ if (!opts::diadump::ShowClassHierarchy)
+ Ids &= ~(PdbSymbolIdField::ClassParent | PdbSymbolIdField::LexicalParent);
+
+ for (PDB_SymType ST : SymTypes) {
+ auto Children = GlobalScope->findAllChildren(ST);
+ while (auto Child = Children->getNext()) {
+ outs() << "{";
+ Child->defaultDump(outs(), 2, Ids, Recurse);
+
+ diaDumpChildren<PDBSymbolTypeEnum, PDBSymbolData>(*Child, Ids, Recurse);
+ outs() << "\n}\n";
+ }
+ }
+}
+
static void dumpPretty(StringRef Path) {
std::unique_ptr<IPDBSession> Session;
@@ -1055,7 +1171,9 @@ static void dumpPretty(StringRef Path) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::SectionHeader).get()
<< "---COMPILANDS---";
- if (auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>()) {
+ auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>();
+
+ if (Compilands) {
Printer.Indent();
CompilandDumper Dumper(Printer);
CompilandDumpFlags options = CompilandDumper::Flags::None;
@@ -1067,7 +1185,9 @@ static void dumpPretty(StringRef Path) {
}
}
- if (opts::pretty::Classes || opts::pretty::Enums || opts::pretty::Typedefs) {
+ if (opts::pretty::Classes || opts::pretty::Enums || opts::pretty::Typedefs ||
+ opts::pretty::Funcsigs || opts::pretty::Pointers ||
+ opts::pretty::Arrays || opts::pretty::VTShapes) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---TYPES---";
Printer.Indent();
@@ -1104,8 +1224,7 @@ static void dumpPretty(StringRef Path) {
std::vector<std::unique_ptr<PDBSymbolFunc>> Funcs;
while (auto Func = Functions->getNext())
Funcs.push_back(std::move(Func));
- llvm::sort(Funcs.begin(), Funcs.end(),
- opts::pretty::compareFunctionSymbols);
+ llvm::sort(Funcs, opts::pretty::compareFunctionSymbols);
for (const auto &Func : Funcs) {
Printer.NewLine();
Dumper.start(*Func, FunctionDumper::PointerType::None);
@@ -1123,8 +1242,7 @@ static void dumpPretty(StringRef Path) {
std::vector<std::unique_ptr<PDBSymbolData>> Datas;
while (auto Var = Vars->getNext())
Datas.push_back(std::move(Var));
- llvm::sort(Datas.begin(), Datas.end(),
- opts::pretty::compareDataSymbols);
+ llvm::sort(Datas, opts::pretty::compareDataSymbols);
for (const auto &Var : Datas)
Dumper.start(*Var);
}
@@ -1162,6 +1280,7 @@ static void dumpPretty(StringRef Path) {
dumpInjectedSources(Printer, *Session);
}
+ Printer.NewLine();
outs().flush();
}
@@ -1211,7 +1330,9 @@ static void mergePdbs() {
OutFile = opts::merge::InputFilenames[0];
llvm::sys::path::replace_extension(OutFile, "merged.pdb");
}
- ExitOnErr(Builder.commit(OutFile));
+
+ codeview::GUID IgnoredOutGuid;
+ ExitOnErr(Builder.commit(OutFile, &IgnoredOutGuid));
}
static void explain() {
@@ -1323,6 +1444,7 @@ int main(int Argc, const char **Argv) {
if (opts::DumpSubcommand) {
if (opts::dump::RawAll) {
opts::dump::DumpGlobals = true;
+ opts::dump::DumpFpo = true;
opts::dump::DumpInlineeLines = true;
opts::dump::DumpIds = true;
opts::dump::DumpIdExtras = true;
@@ -1356,6 +1478,7 @@ int main(int Argc, const char **Argv) {
opts::pdb2yaml::DbiStream = true;
opts::pdb2yaml::TpiStream = true;
opts::pdb2yaml::IpiStream = true;
+ opts::pdb2yaml::PublicsStream = true;
opts::pdb2yaml::DumpModules = true;
opts::pdb2yaml::DumpModuleFiles = true;
opts::pdb2yaml::DumpModuleSyms = true;
@@ -1382,8 +1505,8 @@ int main(int Argc, const char **Argv) {
opts::yaml2pdb::YamlPdbOutputFile = OutputFilename.str();
}
yamlToPdb(opts::yaml2pdb::InputFilename);
- } else if (opts::AnalyzeSubcommand) {
- dumpAnalysis(opts::analyze::InputFilename.front());
+ } else if (opts::DiaDumpSubcommand) {
+ llvm::for_each(opts::diadump::InputFilenames, dumpDia);
} else if (opts::PrettySubcommand) {
if (opts::pretty::Lines)
opts::pretty::Compilands = true;
@@ -1401,6 +1524,8 @@ int main(int Argc, const char **Argv) {
opts::pretty::Classes = true;
opts::pretty::Typedefs = true;
opts::pretty::Enums = true;
+ opts::pretty::Pointers = true;
+ opts::pretty::Funcsigs = true;
}
// When adding filters for excluded compilands and types, we need to
diff --git a/tools/llvm-pdbutil/llvm-pdbutil.h b/tools/llvm-pdbutil/llvm-pdbutil.h
index 7496adaeb62f..a57cc51d7fd7 100644
--- a/tools/llvm-pdbutil/llvm-pdbutil.h
+++ b/tools/llvm-pdbutil/llvm-pdbutil.h
@@ -82,7 +82,11 @@ extern llvm::cl::opt<bool> Symbols;
extern llvm::cl::opt<bool> Globals;
extern llvm::cl::opt<bool> Classes;
extern llvm::cl::opt<bool> Enums;
+extern llvm::cl::opt<bool> Funcsigs;
+extern llvm::cl::opt<bool> Arrays;
extern llvm::cl::opt<bool> Typedefs;
+extern llvm::cl::opt<bool> Pointers;
+extern llvm::cl::opt<bool> VTShapes;
extern llvm::cl::opt<bool> All;
extern llvm::cl::opt<bool> ExcludeCompilerGenerated;
@@ -160,10 +164,12 @@ extern llvm::cl::opt<bool> DumpIdExtras;
extern llvm::cl::list<uint32_t> DumpIdIndex;
extern llvm::cl::opt<uint32_t> DumpModi;
extern llvm::cl::opt<bool> JustMyCode;
+extern llvm::cl::opt<bool> DontResolveForwardRefs;
extern llvm::cl::opt<bool> DumpSymbols;
extern llvm::cl::opt<bool> DumpSymRecordBytes;
extern llvm::cl::opt<bool> DumpGSIRecords;
extern llvm::cl::opt<bool> DumpGlobals;
+extern llvm::cl::list<std::string> DumpGlobalNames;
extern llvm::cl::opt<bool> DumpGlobalExtras;
extern llvm::cl::opt<bool> DumpPublics;
extern llvm::cl::opt<bool> DumpPublicExtras;
@@ -171,6 +177,7 @@ extern llvm::cl::opt<bool> DumpSectionContribs;
extern llvm::cl::opt<bool> DumpSectionMap;
extern llvm::cl::opt<bool> DumpModules;
extern llvm::cl::opt<bool> DumpModuleFiles;
+extern llvm::cl::opt<bool> DumpFpo;
extern llvm::cl::opt<bool> RawAll;
}
@@ -185,6 +192,7 @@ extern llvm::cl::opt<bool> PdbStream;
extern llvm::cl::opt<bool> DbiStream;
extern llvm::cl::opt<bool> TpiStream;
extern llvm::cl::opt<bool> IpiStream;
+extern llvm::cl::opt<bool> PublicsStream;
extern llvm::cl::list<std::string> InputFilename;
extern llvm::cl::opt<bool> DumpModules;
extern llvm::cl::opt<bool> DumpModuleFiles;
diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp
index 1a0b9e127bbc..c25cbc2b64df 100644
--- a/tools/llvm-profdata/llvm-profdata.cpp
+++ b/tools/llvm-profdata/llvm-profdata.cpp
@@ -123,6 +123,47 @@ static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
}
}
+namespace {
+/// A remapper from original symbol names to new symbol names based on a file
+/// containing a list of mappings from old name to new name.
+class SymbolRemapper {
+ std::unique_ptr<MemoryBuffer> File;
+ DenseMap<StringRef, StringRef> RemappingTable;
+
+public:
+ /// Build a SymbolRemapper from a file containing a list of old/new symbols.
+ static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) {
+ auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
+ if (!BufOrError)
+ exitWithErrorCode(BufOrError.getError(), InputFile);
+
+ auto Remapper = llvm::make_unique<SymbolRemapper>();
+ Remapper->File = std::move(BufOrError.get());
+
+ for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#');
+ !LineIt.is_at_eof(); ++LineIt) {
+ std::pair<StringRef, StringRef> Parts = LineIt->split(' ');
+ if (Parts.first.empty() || Parts.second.empty() ||
+ Parts.second.count(' ')) {
+ exitWithError("unexpected line in remapping file",
+ (InputFile + ":" + Twine(LineIt.line_number())).str(),
+ "expected 'old_symbol new_symbol'");
+ }
+ Remapper->RemappingTable.insert(Parts);
+ }
+ return Remapper;
+ }
+
+ /// Attempt to map the given old symbol into a new symbol.
+ ///
+ /// \return The new symbol, or \p Name if no such symbol was found.
+ StringRef operator()(StringRef Name) {
+ StringRef New = RemappingTable.lookup(Name);
+ return New.empty() ? Name : New;
+ }
+};
+}
+
struct WeightedFile {
std::string Filename;
uint64_t Weight;
@@ -161,7 +202,8 @@ static bool isFatalError(instrprof_error IPE) {
}
/// Load an input into a writer context.
-static void loadInput(const WeightedFile &Input, WriterContext *WC) {
+static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
+ WriterContext *WC) {
std::unique_lock<std::mutex> CtxGuard{WC->Lock};
// If there's a pending hard error, don't do more work.
@@ -192,6 +234,8 @@ static void loadInput(const WeightedFile &Input, WriterContext *WC) {
}
for (auto &I : *Reader) {
+ if (Remapper)
+ I.Name = (*Remapper)(I.Name);
const StringRef FuncName = I.Name;
bool Reported = false;
WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) {
@@ -236,6 +280,7 @@ static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
}
static void mergeInstrProfile(const WeightedFileVector &Inputs,
+ SymbolRemapper *Remapper,
StringRef OutputFilename,
ProfileFormat OutputFormat, bool OutputSparse,
unsigned NumThreads) {
@@ -267,14 +312,14 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
if (NumThreads == 1) {
for (const auto &Input : Inputs)
- loadInput(Input, Contexts[0].get());
+ loadInput(Input, Remapper, Contexts[0].get());
} else {
ThreadPool Pool(NumThreads);
// Load the inputs in parallel (N/NumThreads serial steps).
unsigned Ctx = 0;
for (const auto &Input : Inputs) {
- Pool.async(loadInput, Input, Contexts[Ctx].get());
+ Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get());
Ctx = (Ctx + 1) % NumThreads;
}
Pool.wait();
@@ -322,11 +367,43 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
}
}
+/// Make a copy of the given function samples with all symbol names remapped
+/// by the provided symbol remapper.
+static sampleprof::FunctionSamples
+remapSamples(const sampleprof::FunctionSamples &Samples,
+ SymbolRemapper &Remapper, sampleprof_error &Error) {
+ sampleprof::FunctionSamples Result;
+ Result.setName(Remapper(Samples.getName()));
+ Result.addTotalSamples(Samples.getTotalSamples());
+ Result.addHeadSamples(Samples.getHeadSamples());
+ for (const auto &BodySample : Samples.getBodySamples()) {
+ Result.addBodySamples(BodySample.first.LineOffset,
+ BodySample.first.Discriminator,
+ BodySample.second.getSamples());
+ for (const auto &Target : BodySample.second.getCallTargets()) {
+ Result.addCalledTargetSamples(BodySample.first.LineOffset,
+ BodySample.first.Discriminator,
+ Remapper(Target.first()), Target.second);
+ }
+ }
+ for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
+ sampleprof::FunctionSamplesMap &Target =
+ Result.functionSamplesAt(CallsiteSamples.first);
+ for (const auto &Callsite : CallsiteSamples.second) {
+ sampleprof::FunctionSamples Remapped =
+ remapSamples(Callsite.second, Remapper, Error);
+ MergeResult(Error, Target[Remapped.getName()].merge(Remapped));
+ }
+ }
+ return Result;
+}
+
static sampleprof::SampleProfileFormat FormatMap[] = {
sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Compact_Binary,
sampleprof::SPF_GCC, sampleprof::SPF_Binary};
static void mergeSampleProfile(const WeightedFileVector &Inputs,
+ SymbolRemapper *Remapper,
StringRef OutputFilename,
ProfileFormat OutputFormat) {
using namespace sampleprof;
@@ -357,9 +434,13 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs,
for (StringMap<FunctionSamples>::iterator I = Profiles.begin(),
E = Profiles.end();
I != E; ++I) {
- StringRef FName = I->first();
- FunctionSamples &Samples = I->second;
- sampleprof_error Result = ProfileMap[FName].merge(Samples, Input.Weight);
+ sampleprof_error Result = sampleprof_error::success;
+ FunctionSamples Remapped =
+ Remapper ? remapSamples(I->second, *Remapper, Result)
+ : FunctionSamples();
+ FunctionSamples &Samples = Remapper ? Remapped : I->second;
+ StringRef FName = Samples.getName();
+ MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight));
if (Result != sampleprof_error::success) {
std::error_code EC = make_error_code(Result);
handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName);
@@ -461,6 +542,10 @@ static int merge_main(int argc, const char *argv[]) {
cl::opt<bool> DumpInputFileList(
"dump-input-file-list", cl::init(false), cl::Hidden,
cl::desc("Dump the list of input files and their weights, then exit"));
+ cl::opt<std::string> RemappingFile("remapping-file", cl::value_desc("file"),
+ cl::desc("Symbol remapping file"));
+ cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"),
+ cl::aliasopt(RemappingFile));
cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
cl::init("-"), cl::Required,
cl::desc("Output file"));
@@ -509,11 +594,16 @@ static int merge_main(int argc, const char *argv[]) {
return 0;
}
+ std::unique_ptr<SymbolRemapper> Remapper;
+ if (!RemappingFile.empty())
+ Remapper = SymbolRemapper::create(RemappingFile);
+
if (ProfileKind == instr)
- mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat,
- OutputSparse, NumThreads);
+ mergeInstrProfile(WeightedInputs, Remapper.get(), OutputFilename,
+ OutputFormat, OutputSparse, NumThreads);
else
- mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat);
+ mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename,
+ OutputFormat);
return 0;
}
@@ -543,13 +633,21 @@ static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,
Stats.ValueSitesHistogram.resize(NV, 0);
Stats.ValueSitesHistogram[NV - 1]++;
}
+
+ uint64_t SiteSum = 0;
+ for (uint32_t V = 0; V < NV; V++)
+ SiteSum += VD[V].Count;
+ if (SiteSum == 0)
+ SiteSum = 1;
+
for (uint32_t V = 0; V < NV; V++) {
- OS << "\t[ " << I << ", ";
+ OS << "\t[ " << format("%2u", I) << ", ";
if (Symtab == nullptr)
- OS << VD[V].Value;
+ OS << format("%4u", VD[V].Value);
else
OS << Symtab->getFuncName(VD[V].Value);
- OS << ", " << VD[V].Count << " ]\n";
+ OS << ", " << format("%10" PRId64, VD[V].Count) << " ] ("
+ << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n";
}
}
}
@@ -572,9 +670,9 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
uint32_t TopN, bool ShowIndirectCallTargets,
bool ShowMemOPSizes, bool ShowDetailedSummary,
std::vector<uint32_t> DetailedSummaryCutoffs,
- bool ShowAllFunctions,
- const std::string &ShowFunction, bool TextFormat,
- raw_fd_ostream &OS) {
+ bool ShowAllFunctions, uint64_t ValueCutoff,
+ bool OnlyListBelow, const std::string &ShowFunction,
+ bool TextFormat, raw_fd_ostream &OS) {
auto ReaderOrErr = InstrProfReader::create(Filename);
std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
if (ShowDetailedSummary && Cutoffs.empty()) {
@@ -587,6 +685,7 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
auto Reader = std::move(ReaderOrErr.get());
bool IsIRInstr = Reader->isIRLevelProfile();
size_t ShownFunctions = 0;
+ size_t BelowCutoffFunctions = 0;
int NumVPKind = IPVK_Last - IPVK_First + 1;
std::vector<ValueSitesStats> VPStats(NumVPKind);
@@ -600,12 +699,21 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
decltype(MinCmp)>
HottestFuncs(MinCmp);
+ if (!TextFormat && OnlyListBelow) {
+ OS << "The list of functions with the maximum counter less than "
+ << ValueCutoff << ":\n";
+ }
+
+ // Add marker so that IR-level instrumentation round-trips properly.
+ if (TextFormat && IsIRInstr)
+ OS << ":ir\n";
+
for (const auto &Func : *Reader) {
bool Show =
ShowAllFunctions || (!ShowFunction.empty() &&
Func.Name.find(ShowFunction) != Func.Name.npos);
- bool doTextFormatDump = (Show && ShowCounts && TextFormat);
+ bool doTextFormatDump = (Show && TextFormat);
if (doTextFormatDump) {
InstrProfSymtab &Symtab = Reader->getSymtab();
@@ -617,11 +725,24 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
assert(Func.Counts.size() > 0 && "function missing entry counter");
Builder.addRecord(Func);
- if (TopN) {
- uint64_t FuncMax = 0;
- for (size_t I = 0, E = Func.Counts.size(); I < E; ++I)
- FuncMax = std::max(FuncMax, Func.Counts[I]);
+ uint64_t FuncMax = 0;
+ uint64_t FuncSum = 0;
+ for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) {
+ FuncMax = std::max(FuncMax, Func.Counts[I]);
+ FuncSum += Func.Counts[I];
+ }
+ if (FuncMax < ValueCutoff) {
+ ++BelowCutoffFunctions;
+ if (OnlyListBelow) {
+ OS << " " << Func.Name << ": (Max = " << FuncMax
+ << " Sum = " << FuncSum << ")\n";
+ }
+ continue;
+ } else if (OnlyListBelow)
+ continue;
+
+ if (TopN) {
if (HottestFuncs.size() == TopN) {
if (HottestFuncs.top().second < FuncMax) {
HottestFuncs.pop();
@@ -632,7 +753,6 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
}
if (Show) {
-
if (!ShownFunctions)
OS << "Counters:\n";
@@ -679,7 +799,7 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
if (Reader->hasError())
exitWithError(Reader->getError(), Filename);
- if (ShowCounts && TextFormat)
+ if (TextFormat)
return 0;
std::unique_ptr<ProfileSummary> PS(Builder.getSummary());
OS << "Instrumentation level: "
@@ -687,6 +807,12 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
if (ShowAllFunctions || !ShowFunction.empty())
OS << "Functions shown: " << ShownFunctions << "\n";
OS << "Total functions: " << PS->getNumFunctions() << "\n";
+ if (ValueCutoff > 0) {
+ OS << "Number of functions with maximum count (< " << ValueCutoff
+ << "): " << BelowCutoffFunctions << "\n";
+ OS << "Number of functions with maximum count (>= " << ValueCutoff
+ << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n";
+ }
OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";
OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";
@@ -788,7 +914,14 @@ static int show_main(int argc, const char *argv[]) {
cl::opt<uint32_t> TopNFunctions(
"topn", cl::init(0),
cl::desc("Show the list of functions with the largest internal counts"));
-
+ cl::opt<uint32_t> ValueCutoff(
+ "value-cutoff", cl::init(0),
+ cl::desc("Set the count value cutoff. Functions with the maximum count "
+ "less than this value will not be printed out. (Default is 0)"));
+ cl::opt<bool> OnlyListBelow(
+ "list-below-cutoff", cl::init(false),
+ cl::desc("Only output names of functions whose max count values are "
+ "below the cutoff value"));
cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n");
if (OutputFilename.empty())
@@ -808,7 +941,8 @@ static int show_main(int argc, const char *argv[]) {
return showInstrProfile(Filename, ShowCounts, TopNFunctions,
ShowIndirectCallTargets, ShowMemOPSizes,
ShowDetailedSummary, DetailedSummaryCutoffs,
- ShowAllFunctions, ShowFunction, TextFormat, OS);
+ ShowAllFunctions, ValueCutoff, OnlyListBelow,
+ ShowFunction, TextFormat, OS);
else
return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
ShowFunction, OS);
diff --git a/tools/llvm-rc/Opts.td b/tools/llvm-rc/Opts.td
index 11f40f571037..3ff5ac2d4980 100644
--- a/tools/llvm-rc/Opts.td
+++ b/tools/llvm-rc/Opts.td
@@ -4,7 +4,7 @@ include "llvm/Option/OptParser.td"
// These options seem to be important for the tool
// and should be implemented.
-def FILEOUT : Separate<[ "/", "-" ], "FO">,
+def FILEOUT : JoinedOrSeparate<[ "/", "-" ], "FO">,
HelpText<"Change the output file location.">;
def DEFINE : Separate<[ "/", "-" ], "D">,
diff --git a/tools/llvm-rc/ResourceFileWriter.cpp b/tools/llvm-rc/ResourceFileWriter.cpp
index 4b561940d2ec..7fe95669083b 100644
--- a/tools/llvm-rc/ResourceFileWriter.cpp
+++ b/tools/llvm-rc/ResourceFileWriter.cpp
@@ -514,6 +514,11 @@ Error ResourceFileWriter::visitCharacteristicsStmt(
return Error::success();
}
+Error ResourceFileWriter::visitExStyleStmt(const ExStyleStmt *Stmt) {
+ ObjectData.ExStyle = Stmt->Value;
+ return Error::success();
+}
+
Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) {
RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size"));
RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight"));
@@ -982,7 +987,8 @@ Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
padStream(sizeof(uint32_t));
auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
- uint32_t CtlStyle = TypeInfo.Style | Ctl.Style.getValueOr(0);
+ IntWithNotMask CtlStyle(TypeInfo.Style);
+ CtlStyle |= Ctl.Style.getValueOr(RCInt(0));
uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
// DIALOG(EX) item header prefix.
@@ -990,7 +996,7 @@ Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
struct {
ulittle32_t Style;
ulittle32_t ExtStyle;
- } Prefix{ulittle32_t(CtlStyle), ulittle32_t(CtlExtStyle)};
+ } Prefix{ulittle32_t(CtlStyle.getValue()), ulittle32_t(CtlExtStyle)};
writeObject(Prefix);
} else {
struct {
@@ -998,7 +1004,7 @@ Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
ulittle32_t ExtStyle;
ulittle32_t Style;
} Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
- ulittle32_t(CtlStyle)};
+ ulittle32_t(CtlStyle.getValue())};
writeObject(Prefix);
}
@@ -1065,6 +1071,7 @@ Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
UsedStyle |= StyleCaptionFlag;
const uint16_t DialogExMagic = 0xFFFF;
+ uint32_t ExStyle = ObjectData.ExStyle.getValueOr(0);
// Write DIALOG(EX) header prefix. These are pretty different.
if (!Res->IsExtended) {
@@ -1083,7 +1090,7 @@ Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
ulittle32_t Style;
ulittle32_t ExtStyle;
} Prefix{ulittle32_t(UsedStyle),
- ulittle32_t(0)}; // As of now, we don't keep EXSTYLE.
+ ulittle32_t(ExStyle)};
writeObject(Prefix);
} else {
@@ -1094,7 +1101,7 @@ Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
ulittle32_t ExtStyle;
ulittle32_t Style;
} Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
- ulittle32_t(Res->HelpID), ulittle32_t(0), ulittle32_t(UsedStyle)};
+ ulittle32_t(Res->HelpID), ulittle32_t(ExStyle), ulittle32_t(UsedStyle)};
writeObject(Prefix);
}
@@ -1502,6 +1509,10 @@ ResourceFileWriter::loadFile(StringRef File) const {
SmallString<128> Cwd;
std::unique_ptr<MemoryBuffer> Result;
+ // 0. The file path is absolute and the file exists.
+ if (sys::path::is_absolute(File))
+ return errorOrToExpected(MemoryBuffer::getFile(File, -1, false));
+
// 1. The current working directory.
sys::fs::current_path(Cwd);
Path.assign(Cwd.begin(), Cwd.end());
@@ -1510,8 +1521,7 @@ ResourceFileWriter::loadFile(StringRef File) const {
return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
// 2. The directory of the input resource file, if it is different from the
- // current
- // working directory.
+ // current working directory.
StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath);
Path.assign(InputFileDir.begin(), InputFileDir.end());
sys::path::append(Path, File);
diff --git a/tools/llvm-rc/ResourceFileWriter.h b/tools/llvm-rc/ResourceFileWriter.h
index 4c31d018bde4..36d57da11eb6 100644
--- a/tools/llvm-rc/ResourceFileWriter.h
+++ b/tools/llvm-rc/ResourceFileWriter.h
@@ -63,6 +63,7 @@ public:
Error visitCaptionStmt(const CaptionStmt *) override;
Error visitCharacteristicsStmt(const CharacteristicsStmt *) override;
Error visitClassStmt(const ClassStmt *) override;
+ Error visitExStyleStmt(const ExStyleStmt *) override;
Error visitFontStmt(const FontStmt *) override;
Error visitLanguageStmt(const LanguageResource *) override;
Error visitStyleStmt(const StyleStmt *) override;
@@ -80,6 +81,7 @@ public:
uint32_t VersionInfo;
Optional<uint32_t> Style;
+ Optional<uint32_t> ExStyle;
StringRef Caption;
struct FontInfo {
uint32_t Size;
diff --git a/tools/llvm-rc/ResourceScriptParser.cpp b/tools/llvm-rc/ResourceScriptParser.cpp
index 8cc0b50933c2..c66fc4fc2e70 100644
--- a/tools/llvm-rc/ResourceScriptParser.cpp
+++ b/tools/llvm-rc/ResourceScriptParser.cpp
@@ -114,16 +114,23 @@ void RCParser::consume() {
// An integer description might consist of a single integer or
// an arithmetic expression evaluating to the integer. The expressions
-// can contain the following tokens: <int> ( ) + - | & ~. Their meaning
-// is the same as in C++.
+// can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning
+// is the same as in C++ except for 'not' expression.
// The operators in the original RC implementation have the following
// precedence:
-// 1) Unary operators (- ~),
+// 1) Unary operators (- ~ not),
// 2) Binary operators (+ - & |), with no precedence.
//
+// 'not' expression is mostly useful for style values. It evaluates to 0,
+// but value given to the operator is stored separately from integer value.
+// It's mostly useful for control style expressions and causes bits from
+// default control style to be excluded from generated style. For binary
+// operators the mask from the right operand is applied to the left operand
+// and masks from both operands are combined in operator result.
+//
// The following grammar is used to parse the expressions Exp1:
// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
-// Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
+// Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
// separated by binary operators.)
//
@@ -139,12 +146,15 @@ void RCParser::consume() {
// 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
// 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
-Expected<RCInt> RCParser::readInt() { return parseIntExpr1(); }
+Expected<RCInt> RCParser::readInt() {
+ ASSIGN_OR_RETURN(Value, parseIntExpr1());
+ return (*Value).getValue();
+}
-Expected<RCInt> RCParser::parseIntExpr1() {
+Expected<IntWithNotMask> RCParser::parseIntExpr1() {
// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
- RCInt Result = *FirstResult;
+ IntWithNotMask Result = *FirstResult;
while (!isEof() && look().isBinaryOp()) {
auto OpToken = read();
@@ -175,8 +185,8 @@ Expected<RCInt> RCParser::parseIntExpr1() {
return Result;
}
-Expected<RCInt> RCParser::parseIntExpr2() {
- // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
+Expected<IntWithNotMask> RCParser::parseIntExpr2() {
+ // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
static const char ErrorMsg[] = "'-', '~', integer or '('";
if (isEof())
@@ -205,6 +215,13 @@ Expected<RCInt> RCParser::parseIntExpr2() {
return *Result;
}
+ case Kind::Identifier: {
+ if (!read().value().equals_lower("not"))
+ return getExpectedError(ErrorMsg, true);
+ ASSIGN_OR_RETURN(Result, parseIntExpr2());
+ return IntWithNotMask(0, (*Result).getValue());
+ }
+
default:
return getExpectedError(ErrorMsg);
}
@@ -388,6 +405,8 @@ RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
return parseCaptionStmt();
if (TypeToken->equals_lower("CLASS"))
return parseClassStmt();
+ if (TypeToken->equals_lower("EXSTYLE"))
+ return parseExStyleStmt();
if (TypeToken->equals_lower("FONT"))
return parseFontStmt(StmtsType);
if (TypeToken->equals_lower("STYLE"))
@@ -537,13 +556,13 @@ Expected<Control> RCParser::parseControl() {
RETURN_IF_ERROR(consumeType(Kind::Comma));
IntOrString Class;
- Optional<uint32_t> Style;
+ Optional<IntWithNotMask> Style;
if (ClassUpper == "CONTROL") {
// CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
ASSIGN_OR_RETURN(ClassStr, readString());
RETURN_IF_ERROR(consumeType(Kind::Comma));
Class = *ClassStr;
- ASSIGN_OR_RETURN(StyleVal, readInt());
+ ASSIGN_OR_RETURN(StyleVal, parseIntExpr1());
RETURN_IF_ERROR(consumeType(Kind::Comma));
Style = *StyleVal;
} else {
@@ -555,7 +574,7 @@ Expected<Control> RCParser::parseControl() {
if (ClassUpper != "CONTROL") {
if (consumeOptionalType(Kind::Comma)) {
- ASSIGN_OR_RETURN(Val, readInt());
+ ASSIGN_OR_RETURN(Val, parseIntExpr1());
Style = *Val;
}
}
@@ -817,6 +836,11 @@ RCParser::ParseOptionType RCParser::parseStyleStmt() {
return llvm::make_unique<StyleStmt>(*Arg);
}
+RCParser::ParseOptionType RCParser::parseExStyleStmt() {
+ ASSIGN_OR_RETURN(Arg, readInt());
+ return llvm::make_unique<ExStyleStmt>(*Arg);
+}
+
Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
return make_error<ParserError>(
Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
diff --git a/tools/llvm-rc/ResourceScriptParser.h b/tools/llvm-rc/ResourceScriptParser.h
index 3dd110f7e384..20d8ff9624d9 100644
--- a/tools/llvm-rc/ResourceScriptParser.h
+++ b/tools/llvm-rc/ResourceScriptParser.h
@@ -89,8 +89,8 @@ private:
Expected<IntOrString> readTypeOrName(); // Parse an integer or an identifier.
// Helper integer expression parsing methods.
- Expected<RCInt> parseIntExpr1();
- Expected<RCInt> parseIntExpr2();
+ Expected<IntWithNotMask> parseIntExpr1();
+ Expected<IntWithNotMask> parseIntExpr2();
// Advance the state by one, discarding the current token.
// If the discarded token had an incorrect type, fail.
@@ -172,6 +172,7 @@ private:
ParseOptionType parseVersionStmt();
ParseOptionType parseCaptionStmt();
ParseOptionType parseClassStmt();
+ ParseOptionType parseExStyleStmt();
ParseOptionType parseFontStmt(OptStmtType DialogType);
ParseOptionType parseStyleStmt();
diff --git a/tools/llvm-rc/ResourceScriptStmt.cpp b/tools/llvm-rc/ResourceScriptStmt.cpp
index 728c24b36693..7b8b0def4c9a 100644
--- a/tools/llvm-rc/ResourceScriptStmt.cpp
+++ b/tools/llvm-rc/ResourceScriptStmt.cpp
@@ -151,7 +151,7 @@ raw_ostream &Control::log(raw_ostream &OS) const {
<< ", loc: (" << X << ", " << Y << "), size: [" << Width << ", " << Height
<< "]";
if (Style)
- OS << ", style: " << *Style;
+ OS << ", style: " << (*Style).getValue();
if (ExtStyle)
OS << ", ext. style: " << *ExtStyle;
if (HelpID)
@@ -283,5 +283,9 @@ raw_ostream &StyleStmt::log(raw_ostream &OS) const {
return OS << "Style: " << Value << "\n";
}
+raw_ostream &ExStyleStmt::log(raw_ostream &OS) const {
+ return OS << "ExStyle: " << Value << "\n";
+}
+
} // namespace rc
} // namespace llvm
diff --git a/tools/llvm-rc/ResourceScriptStmt.h b/tools/llvm-rc/ResourceScriptStmt.h
index 2071ac6a9a3a..3ba1f6619eb8 100644
--- a/tools/llvm-rc/ResourceScriptStmt.h
+++ b/tools/llvm-rc/ResourceScriptStmt.h
@@ -67,6 +67,59 @@ public:
}
};
+class IntWithNotMask {
+private:
+ RCInt Value;
+ int32_t NotMask;
+
+public:
+ IntWithNotMask() : IntWithNotMask(RCInt(0)) {}
+ IntWithNotMask(RCInt Value, int32_t NotMask = 0) : Value(Value), NotMask(NotMask) {}
+
+ RCInt getValue() const {
+ return Value;
+ }
+
+ uint32_t getNotMask() const {
+ return NotMask;
+ }
+
+ IntWithNotMask &operator+=(const IntWithNotMask &Rhs) {
+ Value &= ~Rhs.NotMask;
+ Value += Rhs.Value;
+ NotMask |= Rhs.NotMask;
+ return *this;
+ }
+
+ IntWithNotMask &operator-=(const IntWithNotMask &Rhs) {
+ Value &= ~Rhs.NotMask;
+ Value -= Rhs.Value;
+ NotMask |= Rhs.NotMask;
+ return *this;
+ }
+
+ IntWithNotMask &operator|=(const IntWithNotMask &Rhs) {
+ Value &= ~Rhs.NotMask;
+ Value |= Rhs.Value;
+ NotMask |= Rhs.NotMask;
+ return *this;
+ }
+
+ IntWithNotMask &operator&=(const IntWithNotMask &Rhs) {
+ Value &= ~Rhs.NotMask;
+ Value &= Rhs.Value;
+ NotMask |= Rhs.NotMask;
+ return *this;
+ }
+
+ IntWithNotMask operator-() const { return {-Value, NotMask}; }
+ IntWithNotMask operator~() const { return {~Value, 0}; }
+
+ friend raw_ostream &operator<<(raw_ostream &OS, const IntWithNotMask &Int) {
+ return OS << Int.Value;
+ }
+};
+
// A class holding a name - either an integer or a reference to the string.
class IntOrString {
private:
@@ -556,7 +609,8 @@ public:
StringRef Type;
IntOrString Title;
uint32_t ID, X, Y, Width, Height;
- Optional<uint32_t> Style, ExtStyle, HelpID;
+ Optional<IntWithNotMask> Style;
+ Optional<uint32_t> ExtStyle, HelpID;
IntOrString Class;
// Control classes as described in DLGITEMTEMPLATEEX documentation.
@@ -580,7 +634,7 @@ public:
Control(StringRef CtlType, IntOrString CtlTitle, uint32_t CtlID,
uint32_t PosX, uint32_t PosY, uint32_t ItemWidth, uint32_t ItemHeight,
- Optional<uint32_t> ItemStyle, Optional<uint32_t> ExtItemStyle,
+ Optional<IntWithNotMask> ItemStyle, Optional<uint32_t> ExtItemStyle,
Optional<uint32_t> CtlHelpID, IntOrString CtlClass)
: Type(CtlType), Title(CtlTitle), ID(CtlID), X(PosX), Y(PosY),
Width(ItemWidth), Height(ItemHeight), Style(ItemStyle),
@@ -866,6 +920,19 @@ public:
Error visit(Visitor *V) const override { return V->visitStyleStmt(this); }
};
+// EXSTYLE optional statement.
+//
+// Ref: docs.microsoft.com/en-us/windows/desktop/menurc/exstyle-statement
+class ExStyleStmt : public OptionalStmt {
+public:
+ uint32_t Value;
+
+ ExStyleStmt(uint32_t ExStyle) : Value(ExStyle) {}
+ raw_ostream &log(raw_ostream &) const override;
+ Twine getResourceTypeName() const override { return "EXSTYLE"; }
+ Error visit(Visitor *V) const override { return V->visitExStyleStmt(this); }
+};
+
// CLASS optional statement.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380883(v=vs.85).aspx
diff --git a/tools/llvm-rc/ResourceVisitor.h b/tools/llvm-rc/ResourceVisitor.h
index 53ef3c5cf7df..2117b12d8f5f 100644
--- a/tools/llvm-rc/ResourceVisitor.h
+++ b/tools/llvm-rc/ResourceVisitor.h
@@ -24,6 +24,7 @@ class RCResource;
class CaptionStmt;
class ClassStmt;
class CharacteristicsStmt;
+class ExStyleStmt;
class FontStmt;
class LanguageResource;
class StyleStmt;
@@ -46,6 +47,7 @@ public:
virtual Error visitCaptionStmt(const CaptionStmt *) = 0;
virtual Error visitClassStmt(const ClassStmt *) = 0;
virtual Error visitCharacteristicsStmt(const CharacteristicsStmt *) = 0;
+ virtual Error visitExStyleStmt(const ExStyleStmt *) = 0;
virtual Error visitFontStmt(const FontStmt *) = 0;
virtual Error visitLanguageStmt(const LanguageResource *) = 0;
virtual Error visitStyleStmt(const StyleStmt *) = 0;
diff --git a/tools/llvm-rc/llvm-rc.cpp b/tools/llvm-rc/llvm-rc.cpp
index 0448f4519b4c..54997e900a23 100644
--- a/tools/llvm-rc/llvm-rc.cpp
+++ b/tools/llvm-rc/llvm-rc.cpp
@@ -31,6 +31,7 @@
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
#include <system_error>
using namespace llvm;
@@ -85,18 +86,23 @@ int main(int Argc, const char **Argv) {
RcOptTable T;
unsigned MAI, MAC;
- ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1);
+ const char **DashDash = std::find_if(
+ Argv + 1, Argv + Argc, [](StringRef Str) { return Str == "--"; });
+ ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, DashDash);
+
opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
// The tool prints nothing when invoked with no command-line arguments.
if (InputArgs.hasArg(OPT_HELP)) {
- T.PrintHelp(outs(), "rc", "Resource Converter", false);
+ T.PrintHelp(outs(), "rc [options] file...", "Resource Converter", false);
return 0;
}
const bool BeVerbose = InputArgs.hasArg(OPT_VERBOSE);
std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT);
+ if (DashDash != Argv + Argc)
+ InArgsInfo.insert(InArgsInfo.end(), DashDash + 1, Argv + Argc);
if (InArgsInfo.size() != 1) {
fatalError("Exactly one input file should be provided.");
}
diff --git a/tools/llvm-readobj/ARMWinEHPrinter.cpp b/tools/llvm-readobj/ARMWinEHPrinter.cpp
index a90840b22c8d..4b823b816c35 100644
--- a/tools/llvm-readobj/ARMWinEHPrinter.cpp
+++ b/tools/llvm-readobj/ARMWinEHPrinter.cpp
@@ -118,31 +118,57 @@ const size_t Decoder::PDataEntrySize = sizeof(RuntimeFunction);
// TODO name the uops more appropriately
const Decoder::RingEntry Decoder::Ring[] = {
- { 0x80, 0x00, &Decoder::opcode_0xxxxxxx }, // UOP_STACK_FREE (16-bit)
- { 0xc0, 0x80, &Decoder::opcode_10Lxxxxx }, // UOP_POP (32-bit)
- { 0xf0, 0xc0, &Decoder::opcode_1100xxxx }, // UOP_STACK_SAVE (16-bit)
- { 0xf8, 0xd0, &Decoder::opcode_11010Lxx }, // UOP_POP (16-bit)
- { 0xf8, 0xd8, &Decoder::opcode_11011Lxx }, // UOP_POP (32-bit)
- { 0xf8, 0xe0, &Decoder::opcode_11100xxx }, // UOP_VPOP (32-bit)
- { 0xfc, 0xe8, &Decoder::opcode_111010xx }, // UOP_STACK_FREE (32-bit)
- { 0xfe, 0xec, &Decoder::opcode_1110110L }, // UOP_POP (16-bit)
- { 0xff, 0xee, &Decoder::opcode_11101110 }, // UOP_MICROSOFT_SPECIFIC (16-bit)
+ { 0x80, 0x00, 1, &Decoder::opcode_0xxxxxxx }, // UOP_STACK_FREE (16-bit)
+ { 0xc0, 0x80, 2, &Decoder::opcode_10Lxxxxx }, // UOP_POP (32-bit)
+ { 0xf0, 0xc0, 1, &Decoder::opcode_1100xxxx }, // UOP_STACK_SAVE (16-bit)
+ { 0xf8, 0xd0, 1, &Decoder::opcode_11010Lxx }, // UOP_POP (16-bit)
+ { 0xf8, 0xd8, 1, &Decoder::opcode_11011Lxx }, // UOP_POP (32-bit)
+ { 0xf8, 0xe0, 1, &Decoder::opcode_11100xxx }, // UOP_VPOP (32-bit)
+ { 0xfc, 0xe8, 2, &Decoder::opcode_111010xx }, // UOP_STACK_FREE (32-bit)
+ { 0xfe, 0xec, 2, &Decoder::opcode_1110110L }, // UOP_POP (16-bit)
+ { 0xff, 0xee, 2, &Decoder::opcode_11101110 }, // UOP_MICROSOFT_SPECIFIC (16-bit)
// UOP_PUSH_MACHINE_FRAME
// UOP_PUSH_CONTEXT
// UOP_PUSH_TRAP_FRAME
// UOP_REDZONE_RESTORE_LR
- { 0xff, 0xef, &Decoder::opcode_11101111 }, // UOP_LDRPC_POSTINC (32-bit)
- { 0xff, 0xf5, &Decoder::opcode_11110101 }, // UOP_VPOP (32-bit)
- { 0xff, 0xf6, &Decoder::opcode_11110110 }, // UOP_VPOP (32-bit)
- { 0xff, 0xf7, &Decoder::opcode_11110111 }, // UOP_STACK_RESTORE (16-bit)
- { 0xff, 0xf8, &Decoder::opcode_11111000 }, // UOP_STACK_RESTORE (16-bit)
- { 0xff, 0xf9, &Decoder::opcode_11111001 }, // UOP_STACK_RESTORE (32-bit)
- { 0xff, 0xfa, &Decoder::opcode_11111010 }, // UOP_STACK_RESTORE (32-bit)
- { 0xff, 0xfb, &Decoder::opcode_11111011 }, // UOP_NOP (16-bit)
- { 0xff, 0xfc, &Decoder::opcode_11111100 }, // UOP_NOP (32-bit)
- { 0xff, 0xfd, &Decoder::opcode_11111101 }, // UOP_NOP (16-bit) / END
- { 0xff, 0xfe, &Decoder::opcode_11111110 }, // UOP_NOP (32-bit) / END
- { 0xff, 0xff, &Decoder::opcode_11111111 }, // UOP_END
+ { 0xff, 0xef, 2, &Decoder::opcode_11101111 }, // UOP_LDRPC_POSTINC (32-bit)
+ { 0xff, 0xf5, 2, &Decoder::opcode_11110101 }, // UOP_VPOP (32-bit)
+ { 0xff, 0xf6, 2, &Decoder::opcode_11110110 }, // UOP_VPOP (32-bit)
+ { 0xff, 0xf7, 3, &Decoder::opcode_11110111 }, // UOP_STACK_RESTORE (16-bit)
+ { 0xff, 0xf8, 4, &Decoder::opcode_11111000 }, // UOP_STACK_RESTORE (16-bit)
+ { 0xff, 0xf9, 3, &Decoder::opcode_11111001 }, // UOP_STACK_RESTORE (32-bit)
+ { 0xff, 0xfa, 4, &Decoder::opcode_11111010 }, // UOP_STACK_RESTORE (32-bit)
+ { 0xff, 0xfb, 1, &Decoder::opcode_11111011 }, // UOP_NOP (16-bit)
+ { 0xff, 0xfc, 1, &Decoder::opcode_11111100 }, // UOP_NOP (32-bit)
+ { 0xff, 0xfd, 1, &Decoder::opcode_11111101 }, // UOP_NOP (16-bit) / END
+ { 0xff, 0xfe, 1, &Decoder::opcode_11111110 }, // UOP_NOP (32-bit) / END
+ { 0xff, 0xff, 1, &Decoder::opcode_11111111 }, // UOP_END
+};
+
+
+// Unwind opcodes for ARM64.
+// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
+const Decoder::RingEntry Decoder::Ring64[] = {
+ { 0xe0, 0x00, 1, &Decoder::opcode_alloc_s },
+ { 0xe0, 0x20, 1, &Decoder::opcode_save_r19r20_x },
+ { 0xc0, 0x40, 1, &Decoder::opcode_save_fplr },
+ { 0xc0, 0x80, 1, &Decoder::opcode_save_fplr_x },
+ { 0xf8, 0xc0, 2, &Decoder::opcode_alloc_m },
+ { 0xfc, 0xc8, 2, &Decoder::opcode_save_regp },
+ { 0xfc, 0xcc, 2, &Decoder::opcode_save_regp_x },
+ { 0xfc, 0xd0, 2, &Decoder::opcode_save_reg },
+ { 0xfe, 0xd4, 2, &Decoder::opcode_save_reg_x },
+ { 0xfe, 0xd6, 2, &Decoder::opcode_save_lrpair },
+ { 0xfe, 0xd8, 2, &Decoder::opcode_save_fregp },
+ { 0xfe, 0xda, 2, &Decoder::opcode_save_fregp_x },
+ { 0xfe, 0xdc, 2, &Decoder::opcode_save_freg },
+ { 0xff, 0xde, 2, &Decoder::opcode_save_freg_x },
+ { 0xff, 0xe0, 4, &Decoder::opcode_alloc_l },
+ { 0xff, 0xe1, 1, &Decoder::opcode_setfp },
+ { 0xff, 0xe2, 2, &Decoder::opcode_addfp },
+ { 0xff, 0xe3, 1, &Decoder::opcode_nop },
+ { 0xff, 0xe4, 1, &Decoder::opcode_end },
+ { 0xff, 0xe5, 1, &Decoder::opcode_end_c },
};
void Decoder::printRegisters(const std::pair<uint16_t, uint32_t> &RegisterMask) {
@@ -493,18 +519,291 @@ bool Decoder::opcode_11111111(const uint8_t *OC, unsigned &Offset,
return true;
}
+// ARM64 unwind codes start here.
+bool Decoder::opcode_alloc_s(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t NumBytes = (OC[Offset] & 0x1F) << 4;
+ SW.startLine() << format("0x%02x ; %s sp, #%u\n", OC[Offset],
+ static_cast<const char *>(Prologue ? "sub" : "add"),
+ NumBytes);
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_save_r19r20_x(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Off = (OC[Offset] & 0x1F) << 3;
+ if (Prologue)
+ SW.startLine() << format(
+ "0x%02x ; stp x19, x20, [sp, #-%u]!\n", OC[Offset], Off);
+ else
+ SW.startLine() << format(
+ "0x%02x ; ldp x19, x20, [sp], #%u\n", OC[Offset], Off);
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_save_fplr(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Off = (OC[Offset] & 0x3F) << 3;
+ SW.startLine() << format(
+ "0x%02x ; %s x29, x30, [sp, #%u]\n", OC[Offset],
+ static_cast<const char *>(Prologue ? "stp" : "ldp"), Off);
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_save_fplr_x(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Off = ((OC[Offset] & 0x3F) + 1) << 3;
+ if (Prologue)
+ SW.startLine() << format(
+ "0x%02x ; stp x29, x30, [sp, #-%u]!\n", OC[Offset], Off);
+ else
+ SW.startLine() << format(
+ "0x%02x ; ldp x29, x30, [sp], #%u\n", OC[Offset], Off);
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_alloc_m(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t NumBytes = ((OC[Offset] & 0x07) << 8);
+ NumBytes |= (OC[Offset + 1] & 0xFF);
+ NumBytes <<= 4;
+ SW.startLine() << format("0x%02x%02x ; %s sp, #%u\n",
+ OC[Offset], OC[Offset + 1],
+ static_cast<const char *>(Prologue ? "sub" : "add"),
+ NumBytes);
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_save_regp(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Reg = ((OC[Offset] & 0x03) << 8);
+ Reg |= (OC[Offset + 1] & 0xC0);
+ Reg >>= 6;
+ Reg += 19;
+ uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
+ SW.startLine() << format(
+ "0x%02x%02x ; %s x%u, x%u, [sp, #%u]\n",
+ OC[Offset], OC[Offset + 1],
+ static_cast<const char *>(Prologue ? "stp" : "ldp"), Reg, Reg + 1, Off);
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_save_regp_x(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Reg = ((OC[Offset] & 0x03) << 8);
+ Reg |= (OC[Offset + 1] & 0xC0);
+ Reg >>= 6;
+ Reg += 19;
+ uint32_t Off = ((OC[Offset + 1] & 0x3F) + 1) << 3;
+ if (Prologue)
+ SW.startLine() << format(
+ "0x%02x%02x ; stp x%u, x%u, [sp, #-%u]!\n",
+ OC[Offset], OC[Offset + 1], Reg,
+ Reg + 1, Off);
+ else
+ SW.startLine() << format(
+ "0x%02x%02x ; ldp x%u, x%u, [sp], #%u\n",
+ OC[Offset], OC[Offset + 1], Reg,
+ Reg + 1, Off);
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_save_reg(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Reg = (OC[Offset] & 0x03) << 8;
+ Reg |= (OC[Offset + 1] & 0xC0);
+ Reg >>= 6;
+ Reg += 19;
+ uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
+ SW.startLine() << format("0x%02x%02x ; %s x%u, [sp, #%u]\n",
+ OC[Offset], OC[Offset + 1],
+ static_cast<const char *>(Prologue ? "str" : "ldr"),
+ Reg, Off);
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_save_reg_x(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Reg = (OC[Offset] & 0x01) << 8;
+ Reg |= (OC[Offset + 1] & 0xE0);
+ Reg >>= 5;
+ Reg += 19;
+ uint32_t Off = ((OC[Offset + 1] & 0x1F) + 1) << 3;
+ if (Prologue)
+ SW.startLine() << format("0x%02x%02x ; str x%u, [sp, #%u]!\n",
+ OC[Offset], OC[Offset + 1], Reg, Off);
+ else
+ SW.startLine() << format("0x%02x%02x ; ldr x%u, [sp], #%u\n",
+ OC[Offset], OC[Offset + 1], Reg, Off);
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_save_lrpair(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Reg = (OC[Offset] & 0x01) << 8;
+ Reg |= (OC[Offset + 1] & 0xC0);
+ Reg >>= 6;
+ Reg *= 2;
+ Reg += 19;
+ uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
+ SW.startLine() << format("0x%02x%02x ; %s x%u, lr, [sp, #%u]\n",
+ OC[Offset], OC[Offset + 1],
+ static_cast<const char *>(Prologue ? "stp" : "ldp"),
+ Reg, Off);
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_save_fregp(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Reg = (OC[Offset] & 0x01) << 8;
+ Reg |= (OC[Offset + 1] & 0xC0);
+ Reg >>= 6;
+ Reg += 8;
+ uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
+ SW.startLine() << format("0x%02x%02x ; %s d%u, d%u, [sp, #%u]\n",
+ OC[Offset], OC[Offset + 1],
+ static_cast<const char *>(Prologue ? "stp" : "ldp"),
+ Reg, Reg + 1, Off);
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_save_fregp_x(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Reg = (OC[Offset] & 0x01) << 8;
+ Reg |= (OC[Offset + 1] & 0xC0);
+ Reg >>= 6;
+ Reg += 8;
+ uint32_t Off = ((OC[Offset + 1] & 0x3F) + 1) << 3;
+ if (Prologue)
+ SW.startLine() << format(
+ "0x%02x%02x ; stp d%u, d%u, [sp, #-%u]!\n", OC[Offset],
+ OC[Offset + 1], Reg, Reg + 1, Off);
+ else
+ SW.startLine() << format(
+ "0x%02x%02x ; ldp d%u, d%u, [sp], #%u\n", OC[Offset],
+ OC[Offset + 1], Reg, Reg + 1, Off);
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_save_freg(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Reg = (OC[Offset] & 0x01) << 8;
+ Reg |= (OC[Offset + 1] & 0xC0);
+ Reg >>= 6;
+ Reg += 8;
+ uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
+ SW.startLine() << format("0x%02x%02x ; %s d%u, [sp, #%u]\n",
+ OC[Offset], OC[Offset + 1],
+ static_cast<const char *>(Prologue ? "str" : "ldr"),
+ Reg, Off);
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_save_freg_x(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Reg = ((OC[Offset + 1] & 0xE0) >> 5) + 8;
+ uint32_t Off = ((OC[Offset + 1] & 0x1F) + 1) << 3;
+ if (Prologue)
+ SW.startLine() << format(
+ "0x%02x%02x ; str d%u, [sp, #-%u]!\n", OC[Offset],
+ OC[Offset + 1], Reg, Off);
+ else
+ SW.startLine() << format(
+ "0x%02x%02x ; ldr d%u, [sp], #%u\n", OC[Offset],
+ OC[Offset + 1], Reg, Off);
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_alloc_l(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ unsigned Off =
+ (OC[Offset + 1] << 16) | (OC[Offset + 2] << 8) | (OC[Offset + 3] << 0);
+ Off <<= 4;
+ SW.startLine() << format(
+ "0x%02x%02x%02x%02x ; %s sp, #%u\n", OC[Offset], OC[Offset + 1],
+ OC[Offset + 2], OC[Offset + 3],
+ static_cast<const char *>(Prologue ? "sub" : "add"), Off);
+ Offset += 4;
+ return false;
+}
+
+bool Decoder::opcode_setfp(const uint8_t *OC, unsigned &Offset, unsigned Length,
+ bool Prologue) {
+ SW.startLine() << format("0x%02x ; mov fp, sp\n", OC[Offset]);
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_addfp(const uint8_t *OC, unsigned &Offset, unsigned Length,
+ bool Prologue) {
+ unsigned NumBytes = OC[Offset + 1] << 3;
+ SW.startLine() << format("0x%02x%02x ; add fp, sp, #%u\n",
+ OC[Offset], OC[Offset + 1], NumBytes);
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_nop(const uint8_t *OC, unsigned &Offset, unsigned Length,
+ bool Prologue) {
+ SW.startLine() << format("0x%02x ; nop\n", OC[Offset]);
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_end(const uint8_t *OC, unsigned &Offset, unsigned Length,
+ bool Prologue) {
+ SW.startLine() << format("0x%02x ; end\n", OC[Offset]);
+ ++Offset;
+ return true;
+}
+
+bool Decoder::opcode_end_c(const uint8_t *OC, unsigned &Offset, unsigned Length,
+ bool Prologue) {
+ SW.startLine() << format("0x%02x ; end_c\n", OC[Offset]);
+ ++Offset;
+ return true;
+}
+
void Decoder::decodeOpcodes(ArrayRef<uint8_t> Opcodes, unsigned Offset,
bool Prologue) {
assert((!Prologue || Offset == 0) && "prologue should always use offset 0");
-
+ const RingEntry* DecodeRing = isAArch64 ? Ring64 : Ring;
bool Terminated = false;
for (unsigned OI = Offset, OE = Opcodes.size(); !Terminated && OI < OE; ) {
for (unsigned DI = 0;; ++DI) {
- if ((Opcodes[OI] & Ring[DI].Mask) == Ring[DI].Value) {
- Terminated = (this->*Ring[DI].Routine)(Opcodes.data(), OI, 0, Prologue);
+ if ((isAArch64 && (DI >= array_lengthof(Ring64))) ||
+ (!isAArch64 && (DI >= array_lengthof(Ring)))) {
+ SW.startLine() << format("0x%02x ; Bad opcode!\n",
+ Opcodes.data()[OI]);
+ ++OI;
+ break;
+ }
+
+ if ((Opcodes[OI] & DecodeRing[DI].Mask) == DecodeRing[DI].Value) {
+ if (OI + DecodeRing[DI].Length > OE) {
+ SW.startLine() << format("Opcode 0x%02x goes past the unwind data\n",
+ Opcodes[OI]);
+ OI += DecodeRing[DI].Length;
+ break;
+ }
+ Terminated =
+ (this->*DecodeRing[DI].Routine)(Opcodes.data(), OI, 0, Prologue);
break;
}
- assert(DI < array_lengthof(Ring) && "unhandled opcode");
}
}
}
@@ -520,22 +819,36 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF,
uint64_t Offset = VA - SectionVA;
const ulittle32_t *Data =
reinterpret_cast<const ulittle32_t *>(Contents.data() + Offset);
- const ExceptionDataRecord XData(Data);
+ // Sanity check to ensure that the .xdata header is present.
+ // A header is one or two words, followed by at least one word to describe
+ // the unwind codes. Applicable to both ARM and AArch64.
+ if (Contents.size() - Offset < 8)
+ report_fatal_error(".xdata must be at least 8 bytes in size");
+
+ const ExceptionDataRecord XData(Data, isAArch64);
DictScope XRS(SW, "ExceptionData");
- SW.printNumber("FunctionLength", XData.FunctionLength() << 1);
+ SW.printNumber("FunctionLength",
+ isAArch64 ? XData.FunctionLengthInBytesAArch64() :
+ XData.FunctionLengthInBytesARM());
SW.printNumber("Version", XData.Vers());
SW.printBoolean("ExceptionData", XData.X());
SW.printBoolean("EpiloguePacked", XData.E());
- SW.printBoolean("Fragment", XData.F());
+ if (!isAArch64)
+ SW.printBoolean("Fragment", XData.F());
SW.printNumber(XData.E() ? "EpilogueOffset" : "EpilogueScopes",
XData.EpilogueCount());
- SW.printNumber("ByteCodeLength",
- static_cast<uint64_t>(XData.CodeWords() * sizeof(uint32_t)));
+ uint64_t ByteCodeLength = XData.CodeWords() * sizeof(uint32_t);
+ SW.printNumber("ByteCodeLength", ByteCodeLength);
+
+ if ((int64_t)(Contents.size() - Offset - 4 * HeaderWords(XData) -
+ (XData.E() ? 0 : XData.EpilogueCount() * 4) -
+ (XData.X() ? 8 : 0)) < (int64_t)ByteCodeLength)
+ report_fatal_error("Malformed unwind data");
if (XData.E()) {
ArrayRef<uint8_t> UC = XData.UnwindByteCode();
- if (!XData.F()) {
+ if (isAArch64 || !XData.F()) {
ListScope PS(SW, "Prologue");
decodeOpcodes(UC, 0, /*Prologue=*/true);
}
@@ -544,16 +857,27 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF,
decodeOpcodes(UC, XData.EpilogueCount(), /*Prologue=*/false);
}
} else {
+ {
+ ListScope PS(SW, "Prologue");
+ decodeOpcodes(XData.UnwindByteCode(), 0, /*Prologue=*/true);
+ }
ArrayRef<ulittle32_t> EpilogueScopes = XData.EpilogueScopes();
ListScope ESS(SW, "EpilogueScopes");
for (const EpilogueScope ES : EpilogueScopes) {
DictScope ESES(SW, "EpilogueScope");
SW.printNumber("StartOffset", ES.EpilogueStartOffset());
- SW.printNumber("Condition", ES.Condition());
- SW.printNumber("EpilogueStartIndex", ES.EpilogueStartIndex());
+ if (!isAArch64)
+ SW.printNumber("Condition", ES.Condition());
+ SW.printNumber("EpilogueStartIndex",
+ isAArch64 ? ES.EpilogueStartIndexAArch64()
+ : ES.EpilogueStartIndexARM());
+ if (ES.ES & ~0xffc3ffff)
+ SW.printNumber("ReservedBits", (ES.ES >> 18) & 0xF);
ListScope Opcodes(SW, "Opcodes");
- decodeOpcodes(XData.UnwindByteCode(), ES.EpilogueStartIndex(),
+ decodeOpcodes(XData.UnwindByteCode(),
+ isAArch64 ? ES.EpilogueStartIndexAArch64()
+ : ES.EpilogueStartIndexARM(),
/*Prologue=*/false);
}
}
@@ -565,16 +889,21 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF,
+ (XData.E() ? 0 : XData.EpilogueCount())
+ XData.CodeWords();
- ErrorOr<SymbolRef> Symbol =
- getRelocatedSymbol(COFF, Section, HandlerOffset * sizeof(uint32_t));
+ ErrorOr<SymbolRef> Symbol = getRelocatedSymbol(
+ COFF, Section, Offset + HandlerOffset * sizeof(uint32_t));
if (!Symbol)
Symbol = getSymbol(COFF, Address, /*FunctionOnly=*/true);
+ if (!Symbol) {
+ ListScope EHS(SW, "ExceptionHandler");
+ SW.printString("Routine", "(null)");
+ return true;
+ }
Expected<StringRef> Name = Symbol->getName();
if (!Name) {
std::string Buf;
llvm::raw_string_ostream OS(Buf);
- logAllUnhandledErrors(Name.takeError(), OS, "");
+ logAllUnhandledErrors(Name.takeError(), OS);
OS.flush();
report_fatal_error(Buf);
}
@@ -613,7 +942,7 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
if (!FunctionNameOrErr) {
std::string Buf;
llvm::raw_string_ostream OS(Buf);
- logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS, "");
+ logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS);
OS.flush();
report_fatal_error(Buf);
}
@@ -622,16 +951,13 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
if (!FunctionAddressOrErr) {
std::string Buf;
llvm::raw_string_ostream OS(Buf);
- logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS, "");
+ logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS);
OS.flush();
report_fatal_error(Buf);
}
FunctionAddress = *FunctionAddressOrErr;
} else {
- const pe32_header *PEHeader;
- if (COFF.getPE32Header(PEHeader))
- return false;
- FunctionAddress = PEHeader->ImageBase + RF.BeginAddress;
+ FunctionAddress = COFF.getImageBase() + RF.BeginAddress;
}
SW.printString("Function", formatSymbol(FunctionName, FunctionAddress));
@@ -641,7 +967,7 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
if (!Name) {
std::string Buf;
llvm::raw_string_ostream OS(Buf);
- logAllUnhandledErrors(Name.takeError(), OS, "");
+ logAllUnhandledErrors(Name.takeError(), OS);
OS.flush();
report_fatal_error(Buf);
}
@@ -650,7 +976,7 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
if (!AddressOrErr) {
std::string Buf;
llvm::raw_string_ostream OS(Buf);
- logAllUnhandledErrors(AddressOrErr.takeError(), OS, "");
+ logAllUnhandledErrors(AddressOrErr.takeError(), OS);
OS.flush();
report_fatal_error(Buf);
}
@@ -666,22 +992,18 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
}
section_iterator SI = *SIOrErr;
- return dumpXDataRecord(COFF, *SI, FunctionAddress, Address);
+ // FIXME: Do we need to add an offset from the relocation?
+ return dumpXDataRecord(COFF, *SI, FunctionAddress,
+ RF.ExceptionInformationRVA());
} else {
- const pe32_header *PEHeader;
- if (COFF.getPE32Header(PEHeader))
- return false;
-
- uint64_t Address = PEHeader->ImageBase + RF.ExceptionInformationRVA();
+ uint64_t Address = COFF.getImageBase() + RF.ExceptionInformationRVA();
SW.printString("ExceptionRecord", formatSymbol("", Address));
- ErrorOr<SectionRef> Section =
- getSectionContaining(COFF, RF.ExceptionInformationRVA());
+ ErrorOr<SectionRef> Section = getSectionContaining(COFF, Address);
if (!Section)
return false;
- return dumpXDataRecord(COFF, *Section, FunctionAddress,
- RF.ExceptionInformationRVA());
+ return dumpXDataRecord(COFF, *Section, FunctionAddress, Address);
}
}
@@ -703,7 +1025,7 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF,
if (!FunctionNameOrErr) {
std::string Buf;
llvm::raw_string_ostream OS(Buf);
- logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS, "");
+ logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS);
OS.flush();
report_fatal_error(Buf);
}
@@ -712,7 +1034,7 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF,
if (!FunctionAddressOrErr) {
std::string Buf;
llvm::raw_string_ostream OS(Buf);
- logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS, "");
+ logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS);
OS.flush();
report_fatal_error(Buf);
}
@@ -725,8 +1047,9 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF,
}
SW.printString("Function", formatSymbol(FunctionName, FunctionAddress));
- SW.printBoolean("Fragment",
- RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment);
+ if (!isAArch64)
+ SW.printBoolean("Fragment",
+ RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment);
SW.printNumber("FunctionLength", RF.FunctionLength());
SW.startLine() << "ReturnType: " << RF.Ret() << '\n';
SW.printBoolean("HomedParameters", RF.H());
@@ -749,6 +1072,10 @@ bool Decoder::dumpProcedureDataEntry(const COFFObjectFile &COFF,
DictScope RFS(SW, "RuntimeFunction");
if (Entry.Flag() == RuntimeFunctionFlag::RFF_Unpacked)
return dumpUnpackedEntry(COFF, Section, Offset, Index, Entry);
+ if (isAArch64) {
+ SW.startLine() << "Packed unwind data not yet supported for ARM64\n";
+ return true;
+ }
return dumpPackedEntry(COFF, Section, Offset, Index, Entry);
}
diff --git a/tools/llvm-readobj/ARMWinEHPrinter.h b/tools/llvm-readobj/ARMWinEHPrinter.h
index 95f521702268..e271a1e6fe77 100644
--- a/tools/llvm-readobj/ARMWinEHPrinter.h
+++ b/tools/llvm-readobj/ARMWinEHPrinter.h
@@ -24,13 +24,16 @@ class Decoder {
ScopedPrinter &SW;
raw_ostream &OS;
+ bool isAArch64;
struct RingEntry {
uint8_t Mask;
uint8_t Value;
+ uint8_t Length;
bool (Decoder::*Routine)(const uint8_t *, unsigned &, unsigned, bool);
};
static const RingEntry Ring[];
+ static const RingEntry Ring64[];
bool opcode_0xxxxxxx(const uint8_t *Opcodes, unsigned &Offset,
unsigned Length, bool Prologue);
@@ -75,6 +78,50 @@ class Decoder {
bool opcode_11111111(const uint8_t *Opcodes, unsigned &Offset,
unsigned Length, bool Prologue);
+ // ARM64 unwind codes start here.
+ bool opcode_alloc_s(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
+ bool Prologue);
+ bool opcode_save_r19r20_x(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_save_fplr(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_save_fplr_x(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_alloc_m(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
+ bool Prologue);
+ bool opcode_save_regp(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_save_regp_x(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_save_reg(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_save_reg_x(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_save_lrpair(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_save_fregp(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_save_fregp_x(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_save_freg(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_save_freg_x(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_alloc_l(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
+ bool Prologue);
+ bool opcode_setfp(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
+ bool Prologue);
+ bool opcode_addfp(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
+ bool Prologue);
+ bool opcode_nop(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
+ bool Prologue);
+ bool opcode_end(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
+ bool Prologue);
+ bool opcode_end_c(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
+ bool Prologue);
+ bool opcode_save_next(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+
void decodeOpcodes(ArrayRef<uint8_t> Opcodes, unsigned Offset,
bool Prologue);
@@ -107,7 +154,9 @@ class Decoder {
const object::SectionRef Section);
public:
- Decoder(ScopedPrinter &SW) : SW(SW), OS(SW.getOStream()) {}
+ Decoder(ScopedPrinter &SW, bool isAArch64) : SW(SW),
+ OS(SW.getOStream()),
+ isAArch64(isAArch64) {}
std::error_code dumpProcedureData(const object::COFFObjectFile &COFF);
};
}
diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp
index 0ed4ccd09f6f..3e2626dad118 100644
--- a/tools/llvm-readobj/COFFDumper.cpp
+++ b/tools/llvm-readobj/COFFDumper.cpp
@@ -50,6 +50,7 @@
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/LEB128.h"
#include "llvm/Support/Win64EH.h"
#include "llvm/Support/raw_ostream.h"
@@ -78,7 +79,7 @@ public:
: ObjDumper(Writer), Obj(Obj), Writer(Writer), Types(100) {}
void printFileHeaders() override;
- void printSections() override;
+ void printSectionHeaders() override;
void printRelocations() override;
void printSymbols() override;
void printDynamicSymbols() override;
@@ -98,6 +99,7 @@ public:
mergeCodeViewTypes(llvm::codeview::MergingTypeTableBuilder &CVIDs,
llvm::codeview::MergingTypeTableBuilder &CVTypes) override;
void printStackMap() const override;
+ void printAddrsig() override;
private:
void printSymbol(const SymbolRef &Sym);
void printRelocation(const SectionRef &Section, const RelocationRef &Reloc,
@@ -177,6 +179,10 @@ private:
DebugStringTableSubsectionRef CVStringTable;
+ /// Track the compilation CPU type. S_COMPILE3 symbol records typically come
+ /// first, but if we don't see one, just assume an X64 CPU type. It is common.
+ CPUType CompilationCPUType = CPUType::X64;
+
ScopedPrinter &Writer;
BinaryByteStream TypeContents;
LazyRandomTypeCollection Types;
@@ -607,8 +613,7 @@ void COFFDumper::cacheRelocations() {
RelocMap[Section].push_back(Reloc);
// Sort relocations by address.
- llvm::sort(RelocMap[Section].begin(), RelocMap[Section].end(),
- relocAddressLess);
+ llvm::sort(RelocMap[Section], relocAddressLess);
}
}
@@ -749,7 +754,7 @@ void COFFDumper::printCOFFDebugDirectory() {
W.printNumber("PDBAge", DebugInfo->PDB70.Age);
W.printString("PDBFileName", PDBFileName);
}
- } else {
+ } else if (D.SizeOfData != 0) {
// FIXME: Type values of 12 and 13 are commonly observed but are not in
// the documented type enum. Figure out what they mean.
ArrayRef<uint8_t> RawData;
@@ -954,7 +959,7 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName,
StringMap<StringRef> FunctionLineTables;
ListScope D(W, "CodeViewDebugInfo");
- // Print the section to allow correlation with printSections.
+ // Print the section to allow correlation with printSectionHeaders.
W.printNumber("Section", SectionName, Obj->getSectionID(Section));
uint32_t Magic;
@@ -1060,10 +1065,28 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName,
W.printHex("LocalSize", FD.LocalSize);
W.printHex("ParamsSize", FD.ParamsSize);
W.printHex("MaxStackSize", FD.MaxStackSize);
- W.printString("FrameFunc", FrameFunc);
W.printHex("PrologSize", FD.PrologSize);
W.printHex("SavedRegsSize", FD.SavedRegsSize);
W.printFlags("Flags", FD.Flags, makeArrayRef(FrameDataFlags));
+
+ // The FrameFunc string is a small RPN program. It can be broken up into
+ // statements that end in the '=' operator, which assigns the value on
+ // the top of the stack to the previously pushed variable. Variables can
+ // be temporary values ($T0) or physical registers ($esp). Print each
+ // assignment on its own line to make these programs easier to read.
+ {
+ ListScope FFS(W, "FrameFunc");
+ while (!FrameFunc.empty()) {
+ size_t EqOrEnd = FrameFunc.find('=');
+ if (EqOrEnd == StringRef::npos)
+ EqOrEnd = FrameFunc.size();
+ else
+ ++EqOrEnd;
+ StringRef Stmt = FrameFunc.substr(0, EqOrEnd);
+ W.printString(Stmt);
+ FrameFunc = FrameFunc.drop_front(EqOrEnd).trim();
+ }
+ }
}
break;
}
@@ -1130,7 +1153,7 @@ void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection,
auto CODD = llvm::make_unique<COFFObjectDumpDelegate>(*this, Section, Obj,
SectionContents);
CVSymbolDumper CVSD(W, Types, CodeViewContainer::ObjectFile, std::move(CODD),
- opts::CodeViewSubsectionBytes);
+ CompilationCPUType, opts::CodeViewSubsectionBytes);
CVSymbolArray Symbols;
BinaryStreamReader Reader(BinaryData, llvm::support::little);
if (auto EC = Reader.readArray(Symbols, Reader.getLength())) {
@@ -1143,6 +1166,7 @@ void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection,
W.flush();
error(std::move(EC));
}
+ CompilationCPUType = CVSD.getCompilationCPUType();
W.flush();
}
@@ -1224,7 +1248,9 @@ void COFFDumper::mergeCodeViewTypes(MergingTypeTableBuilder &CVIDs,
error(object_error::parse_failed);
}
SmallVector<TypeIndex, 128> SourceToDest;
- if (auto EC = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types))
+ Optional<uint32_t> PCHSignature;
+ if (auto EC = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types,
+ PCHSignature))
return error(std::move(EC));
}
}
@@ -1253,7 +1279,7 @@ void COFFDumper::printCodeViewTypeSection(StringRef SectionName,
W.flush();
}
-void COFFDumper::printSections() {
+void COFFDumper::printSectionHeaders() {
ListScope SectionsD(W, "Sections");
int SectionNumber = 0;
for (const SectionRef &Sec : Obj->sections()) {
@@ -1339,10 +1365,12 @@ void COFFDumper::printRelocation(const SectionRef &Section,
StringRef SymbolName;
Reloc.getTypeName(RelocName);
symbol_iterator Symbol = Reloc.getSymbol();
+ int64_t SymbolIndex = -1;
if (Symbol != Obj->symbol_end()) {
Expected<StringRef> SymbolNameOrErr = Symbol->getName();
error(errorToErrorCode(SymbolNameOrErr.takeError()));
SymbolName = *SymbolNameOrErr;
+ SymbolIndex = Obj->getSymbolIndex(Obj->getCOFFSymbol(*Symbol));
}
if (opts::ExpandRelocs) {
@@ -1350,11 +1378,13 @@ void COFFDumper::printRelocation(const SectionRef &Section,
W.printHex("Offset", Offset);
W.printNumber("Type", RelocName, RelocType);
W.printString("Symbol", SymbolName.empty() ? "-" : SymbolName);
+ W.printNumber("SymbolIndex", SymbolIndex);
} else {
raw_ostream& OS = W.startLine();
OS << W.hex(Offset)
<< " " << RelocName
<< " " << (SymbolName.empty() ? "-" : SymbolName)
+ << " (" << SymbolIndex << ")"
<< "\n";
}
}
@@ -1525,8 +1555,10 @@ void COFFDumper::printUnwindInfo() {
Dumper.printData(Ctx);
break;
}
+ case COFF::IMAGE_FILE_MACHINE_ARM64:
case COFF::IMAGE_FILE_MACHINE_ARMNT: {
- ARM::WinEH::Decoder Decoder(W);
+ ARM::WinEH::Decoder Decoder(W, Obj->getMachine() ==
+ COFF::IMAGE_FILE_MACHINE_ARM64);
Decoder.dumpProcedureData(*Obj);
break;
}
@@ -1830,6 +1862,49 @@ void COFFDumper::printStackMap() const {
StackMapV2Parser<support::big>(StackMapContentsArray));
}
+void COFFDumper::printAddrsig() {
+ object::SectionRef AddrsigSection;
+ for (auto Sec : Obj->sections()) {
+ StringRef Name;
+ Sec.getName(Name);
+ if (Name == ".llvm_addrsig") {
+ AddrsigSection = Sec;
+ break;
+ }
+ }
+
+ if (AddrsigSection == object::SectionRef())
+ return;
+
+ StringRef AddrsigContents;
+ AddrsigSection.getContents(AddrsigContents);
+ ArrayRef<uint8_t> AddrsigContentsArray(
+ reinterpret_cast<const uint8_t*>(AddrsigContents.data()),
+ AddrsigContents.size());
+
+ ListScope L(W, "Addrsig");
+ auto *Cur = reinterpret_cast<const uint8_t *>(AddrsigContents.begin());
+ auto *End = reinterpret_cast<const uint8_t *>(AddrsigContents.end());
+ while (Cur != End) {
+ unsigned Size;
+ const char *Err;
+ uint64_t SymIndex = decodeULEB128(Cur, &Size, End, &Err);
+ if (Err)
+ reportError(Err);
+
+ Expected<COFFSymbolRef> Sym = Obj->getSymbol(SymIndex);
+ StringRef SymName;
+ std::error_code EC = errorToErrorCode(Sym.takeError());
+ if (EC || (EC = Obj->getSymbolName(*Sym, SymName))) {
+ SymName = "";
+ error(EC);
+ }
+
+ W.printNumber("Sym", SymName, SymIndex);
+ Cur += Size;
+ }
+}
+
void llvm::dumpCodeViewMergedTypes(
ScopedPrinter &Writer, llvm::codeview::MergingTypeTableBuilder &IDTable,
llvm::codeview::MergingTypeTableBuilder &CVTypes) {
diff --git a/tools/llvm-readobj/DwarfCFIEHPrinter.h b/tools/llvm-readobj/DwarfCFIEHPrinter.h
index 5a1eef1d007d..d91d764c4d0a 100644
--- a/tools/llvm-readobj/DwarfCFIEHPrinter.h
+++ b/tools/llvm-readobj/DwarfCFIEHPrinter.h
@@ -16,6 +16,7 @@
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/ELFTypes.h"
+#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/Debug.h"
@@ -31,15 +32,15 @@ namespace DwarfCFIEH {
template <typename ELFT>
class PrinterContext {
ScopedPrinter &W;
- const object::ELFFile<ELFT> *Obj;
+ const object::ELFObjectFile<ELFT> *ObjF;
void printEHFrameHdr(uint64_t Offset, uint64_t Address, uint64_t Size) const;
void printEHFrame(const typename ELFT::Shdr *EHFrameShdr) const;
public:
- PrinterContext(ScopedPrinter &W, const object::ELFFile<ELFT> *Obj)
- : W(W), Obj(Obj) {}
+ PrinterContext(ScopedPrinter &W, const object::ELFObjectFile<ELFT> *ObjF)
+ : W(W), ObjF(ObjF) {}
void printUnwindInformation() const;
};
@@ -59,6 +60,7 @@ static const typename ELFO::Elf_Shdr *findSectionByAddress(const ELFO *Obj,
template <typename ELFT>
void PrinterContext<ELFT>::printUnwindInformation() const {
+ const object::ELFFile<ELFT> *Obj = ObjF->getELFFile();
const typename ELFT::Phdr *EHFramePhdr = nullptr;
auto PHs = Obj->program_headers();
@@ -101,6 +103,7 @@ void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset,
W.startLine() << format("Offset: 0x%" PRIx64 "\n", EHFrameHdrOffset);
W.startLine() << format("Size: 0x%" PRIx64 "\n", EHFrameHdrSize);
+ const object::ELFFile<ELFT> *Obj = ObjF->getELFFile();
const auto *EHFrameHdrShdr = findSectionByAddress(Obj, EHFrameHdrAddress);
if (EHFrameHdrShdr) {
auto SectionName = Obj->getSectionName(EHFrameHdrShdr);
@@ -173,6 +176,7 @@ void PrinterContext<ELFT>::printEHFrame(
ShOffset, Address);
W.indent();
+ const object::ELFFile<ELFT> *Obj = ObjF->getELFFile();
auto Result = Obj->getSectionContents(EHFrameShdr);
if (Error E = Result.takeError())
reportError(toString(std::move(E)));
@@ -183,7 +187,8 @@ void PrinterContext<ELFT>::printEHFrame(
Contents.size()),
ELFT::TargetEndianness == support::endianness::little,
ELFT::Is64Bits ? 8 : 4);
- DWARFDebugFrame EHFrame(/*IsEH=*/true, /*EHFrameAddress=*/Address);
+ DWARFDebugFrame EHFrame(Triple::ArchType(ObjF->getArch()), /*IsEH=*/true,
+ /*EHFrameAddress=*/Address);
EHFrame.parse(DE);
for (const auto &Entry : EHFrame) {
diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp
index 645ec2d7e04b..93254717e921 100644
--- a/tools/llvm-readobj/ELFDumper.cpp
+++ b/tools/llvm-readobj/ELFDumper.cpp
@@ -22,12 +22,13 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PointerIntPair.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/ELFObjectFile.h"
@@ -43,6 +44,7 @@
#include "llvm/Support/Endian.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MathExtras.h"
@@ -139,10 +141,10 @@ struct DynRegionInfo {
template<typename ELFT>
class ELFDumper : public ObjDumper {
public:
- ELFDumper(const ELFFile<ELFT> *Obj, ScopedPrinter &Writer);
+ ELFDumper(const object::ELFObjectFile<ELFT> *ObjF, ScopedPrinter &Writer);
void printFileHeaders() override;
- void printSections() override;
+ void printSectionHeaders() override;
void printRelocations() override;
void printDynamicRelocations() override;
void printSymbols() override;
@@ -181,6 +183,7 @@ private:
TYPEDEF_ELF_TYPES(ELFT)
DynRegionInfo checkDRI(DynRegionInfo DRI) {
+ const ELFFile<ELFT> *Obj = ObjF->getELFFile();
if (DRI.Addr < Obj->base() ||
(const uint8_t *)DRI.Addr + DRI.Size > Obj->base() + Obj->getBufSize())
error(llvm::object::object_error::parse_failed);
@@ -188,11 +191,11 @@ private:
}
DynRegionInfo createDRIFrom(const Elf_Phdr *P, uintX_t EntSize) {
- return checkDRI({Obj->base() + P->p_offset, P->p_filesz, EntSize});
+ return checkDRI({ObjF->getELFFile()->base() + P->p_offset, P->p_filesz, EntSize});
}
DynRegionInfo createDRIFrom(const Elf_Shdr *S) {
- return checkDRI({Obj->base() + S->sh_offset, S->sh_size, S->sh_entsize});
+ return checkDRI({ObjF->getELFFile()->base() + S->sh_offset, S->sh_size, S->sh_entsize});
}
void parseDynamicTable(ArrayRef<const Elf_Phdr *> LoadSegments);
@@ -206,7 +209,7 @@ private:
void LoadVersionNeeds(const Elf_Shdr *ec) const;
void LoadVersionDefs(const Elf_Shdr *sec) const;
- const ELFO *Obj;
+ const object::ELFObjectFile<ELFT> *ObjF;
DynRegionInfo DynRelRegion;
DynRegionInfo DynRelaRegion;
DynRegionInfo DynRelrRegion;
@@ -289,6 +292,7 @@ void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) const {
StringRef StrTable, SymtabName;
size_t Entries = 0;
Elf_Sym_Range Syms(nullptr, nullptr);
+ const ELFFile<ELFT> *Obj = ObjF->getELFFile();
if (IsDynamic) {
StrTable = DynamicStringTable;
Syms = dynamic_symbols();
@@ -323,7 +327,7 @@ public:
virtual void printFileHeaders(const ELFFile<ELFT> *Obj) = 0;
virtual void printGroupSections(const ELFFile<ELFT> *Obj) = 0;
virtual void printRelocations(const ELFFile<ELFT> *Obj) = 0;
- virtual void printSections(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printSectionHeaders(const ELFFile<ELFT> *Obj) = 0;
virtual void printSymbols(const ELFFile<ELFT> *Obj) = 0;
virtual void printDynamicSymbols(const ELFFile<ELFT> *Obj) = 0;
virtual void printDynamicRelocations(const ELFFile<ELFT> *Obj) = 0;
@@ -358,7 +362,7 @@ public:
void printFileHeaders(const ELFO *Obj) override;
void printGroupSections(const ELFFile<ELFT> *Obj) override;
void printRelocations(const ELFO *Obj) override;
- void printSections(const ELFO *Obj) override;
+ void printSectionHeaders(const ELFO *Obj) override;
void printSymbols(const ELFO *Obj) override;
void printDynamicSymbols(const ELFO *Obj) override;
void printDynamicRelocations(const ELFO *Obj) override;
@@ -390,6 +394,33 @@ private:
return to_hexString(Value, false);
}
+ template <typename T, typename TEnum>
+ std::string printFlags(T Value, ArrayRef<EnumEntry<TEnum>> EnumValues,
+ TEnum EnumMask1 = {}, TEnum EnumMask2 = {},
+ TEnum EnumMask3 = {}) {
+ std::string Str;
+ for (const auto &Flag : EnumValues) {
+ if (Flag.Value == 0)
+ continue;
+
+ TEnum EnumMask{};
+ if (Flag.Value & EnumMask1)
+ EnumMask = EnumMask1;
+ else if (Flag.Value & EnumMask2)
+ EnumMask = EnumMask2;
+ else if (Flag.Value & EnumMask3)
+ EnumMask = EnumMask3;
+ bool IsEnum = (Flag.Value & EnumMask) != 0;
+ if ((!IsEnum && (Value & Flag.Value) == Flag.Value) ||
+ (IsEnum && (Value & EnumMask) == Flag.Value)) {
+ if (!Str.empty())
+ Str += ", ";
+ Str += Flag.AltName;
+ }
+ }
+ return Str;
+ }
+
formatted_raw_ostream &printField(struct Field F) {
if (F.Column != 0)
OS.PadToColumn(F.Column);
@@ -424,7 +455,7 @@ public:
void printGroupSections(const ELFFile<ELFT> *Obj) override;
void printRelocations(const ELFO *Obj) override;
void printRelocations(const Elf_Shdr *Sec, const ELFO *Obj);
- void printSections(const ELFO *Obj) override;
+ void printSectionHeaders(const ELFO *Obj) override;
void printSymbols(const ELFO *Obj) override;
void printDynamicSymbols(const ELFO *Obj) override;
void printDynamicRelocations(const ELFO *Obj) override;
@@ -451,7 +482,7 @@ private:
namespace llvm {
template <class ELFT>
-static std::error_code createELFDumper(const ELFFile<ELFT> *Obj,
+static std::error_code createELFDumper(const ELFObjectFile<ELFT> *Obj,
ScopedPrinter &Writer,
std::unique_ptr<ObjDumper> &Result) {
Result.reset(new ELFDumper<ELFT>(Obj, Writer));
@@ -463,19 +494,19 @@ std::error_code createELFDumper(const object::ObjectFile *Obj,
std::unique_ptr<ObjDumper> &Result) {
// Little-endian 32-bit
if (const ELF32LEObjectFile *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
- return createELFDumper(ELFObj->getELFFile(), Writer, Result);
+ return createELFDumper(ELFObj, Writer, Result);
// Big-endian 32-bit
if (const ELF32BEObjectFile *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
- return createELFDumper(ELFObj->getELFFile(), Writer, Result);
+ return createELFDumper(ELFObj, Writer, Result);
// Little-endian 64-bit
if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
- return createELFDumper(ELFObj->getELFFile(), Writer, Result);
+ return createELFDumper(ELFObj, Writer, Result);
// Big-endian 64-bit
if (const ELF64BEObjectFile *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
- return createELFDumper(ELFObj->getELFFile(), Writer, Result);
+ return createELFDumper(ELFObj, Writer, Result);
return readobj_error::unsupported_obj_file_format;
}
@@ -488,7 +519,7 @@ template <class ELFT>
void ELFDumper<ELFT>::LoadVersionNeeds(const Elf_Shdr *sec) const {
unsigned vn_size = sec->sh_size; // Size of section in bytes
unsigned vn_count = sec->sh_info; // Number of Verneed entries
- const char *sec_start = (const char *)Obj->base() + sec->sh_offset;
+ const char *sec_start = (const char *)ObjF->getELFFile()->base() + sec->sh_offset;
const char *sec_end = sec_start + vn_size;
// The first Verneed entry is at the start of the section.
const char *p = sec_start;
@@ -522,7 +553,7 @@ template <class ELFT>
void ELFDumper<ELFT>::LoadVersionDefs(const Elf_Shdr *sec) const {
unsigned vd_size = sec->sh_size; // Size of section in bytes
unsigned vd_count = sec->sh_info; // Number of Verdef entries
- const char *sec_start = (const char *)Obj->base() + sec->sh_offset;
+ const char *sec_start = (const char *)ObjF->getELFFile()->base() + sec->sh_offset;
const char *sec_end = sec_start + vd_size;
// The first Verdef entry is at the start of the section.
const char *p = sec_start;
@@ -547,7 +578,7 @@ template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() const {
return;
// Has the VersionMap already been loaded?
- if (VersionMap.size() > 0)
+ if (!VersionMap.empty())
return;
// The first two version indexes are reserved.
@@ -611,9 +642,12 @@ static void printVersionDefinitionSection(ELFDumper<ELFT> *Dumper,
// is determined by DT_VERDEFNUM tag.
unsigned VerDefsNum = 0;
for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table()) {
- if (Dyn.d_tag == DT_VERDEFNUM)
+ if (Dyn.d_tag == DT_VERDEFNUM) {
VerDefsNum = Dyn.d_un.d_val;
+ break;
+ }
}
+
const uint8_t *SecStartAddress =
(const uint8_t *)Obj->base() + Sec->sh_offset;
const uint8_t *SecEndAddress = SecStartAddress + Sec->sh_size;
@@ -664,9 +698,12 @@ static void printVersionDependencySection(ELFDumper<ELFT> *Dumper,
return;
unsigned VerNeedNum = 0;
- for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table())
- if (Dyn.d_tag == DT_VERNEEDNUM)
+ for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table()) {
+ if (Dyn.d_tag == DT_VERNEEDNUM) {
VerNeedNum = Dyn.d_un.d_val;
+ break;
+ }
+ }
const uint8_t *SecData = (const uint8_t *)Obj->base() + Sec->sh_offset;
const typename ELFO::Elf_Shdr *StrTab =
@@ -700,13 +737,13 @@ static void printVersionDependencySection(ELFDumper<ELFT> *Dumper,
template <typename ELFT> void ELFDumper<ELFT>::printVersionInfo() {
// Dump version symbol section.
- printVersionSymbolSection(this, Obj, dot_gnu_version_sec, W);
+ printVersionSymbolSection(this, ObjF->getELFFile(), dot_gnu_version_sec, W);
// Dump version definition section.
- printVersionDefinitionSection(this, Obj, dot_gnu_version_d_sec, W);
+ printVersionDefinitionSection(this, ObjF->getELFFile(), dot_gnu_version_d_sec, W);
// Dump version dependency section.
- printVersionDependencySection(this, Obj, dot_gnu_version_r_sec, W);
+ printVersionDependencySection(this, ObjF->getELFFile(), dot_gnu_version_r_sec, W);
}
template <typename ELFT>
@@ -727,7 +764,7 @@ StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab,
// Get the corresponding version index entry
const Elf_Versym *vs = unwrapOrError(
- Obj->template getEntry<Elf_Versym>(dot_gnu_version_sec, entry_index));
+ ObjF->getELFFile()->template getEntry<Elf_Versym>(dot_gnu_version_sec, entry_index));
size_t version_index = vs->vs_index & ELF::VERSYM_VERSION;
// Special markers for unversioned symbols.
@@ -760,6 +797,7 @@ StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab,
template <typename ELFT>
StringRef ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const {
+ const ELFFile<ELFT> *Obj = ObjF->getELFFile();
StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*DotSymtabSec));
Elf_Sym_Range Syms = unwrapOrError(Obj->symbols(DotSymtabSec));
if (Index >= Syms.size())
@@ -780,8 +818,10 @@ std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol,
bool IsDefault;
StringRef Version = getSymbolVersion(StrTable, &*Symbol, IsDefault);
- FullSymbolName += (IsDefault ? "@@" : "@");
- FullSymbolName += Version;
+ if (!Version.empty()) {
+ FullSymbolName += (IsDefault ? "@@" : "@");
+ FullSymbolName += Version;
+ }
return FullSymbolName;
}
@@ -807,6 +847,7 @@ void ELFDumper<ELFT>::getSectionNameIndex(const Elf_Sym *Symbol,
if (SectionIndex == SHN_XINDEX)
SectionIndex = unwrapOrError(object::getExtendedSymbolTableIndex<ELFT>(
Symbol, FirstSym, ShndxTable));
+ const ELFFile<ELFT> *Obj = ObjF->getELFFile();
const typename ELFT::Shdr *Sec =
unwrapOrError(Obj->getSection(SectionIndex));
SectionName = unwrapOrError(Obj->getSectionName(Sec));
@@ -1167,6 +1208,7 @@ static const char *getElfSegmentType(unsigned Arch, unsigned Type) {
switch (Type) {
LLVM_READOBJ_ENUM_CASE(ELF, PT_ARM_EXIDX);
}
+ break;
case ELF::EM_MIPS:
case ELF::EM_MIPS_RS3_LE:
switch (Type) {
@@ -1175,6 +1217,7 @@ static const char *getElfSegmentType(unsigned Arch, unsigned Type) {
LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_OPTIONS);
LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_ABIFLAGS);
}
+ break;
}
switch (Type) {
@@ -1221,7 +1264,7 @@ static std::string getElfPtType(unsigned Arch, unsigned Type) {
case ELF::EM_ARM:
if (Type == ELF::PT_ARM_EXIDX)
return "EXIDX";
- return "";
+ break;
case ELF::EM_MIPS:
case ELF::EM_MIPS_RS3_LE:
switch (Type) {
@@ -1234,7 +1277,7 @@ static std::string getElfPtType(unsigned Arch, unsigned Type) {
case PT_MIPS_ABIFLAGS:
return "ABIFLAGS";
}
- return "";
+ break;
}
}
return std::string("<unknown>: ") + to_string(format_hex(Type, 1));
@@ -1247,49 +1290,49 @@ static const EnumEntry<unsigned> ElfSegmentFlags[] = {
};
static const EnumEntry<unsigned> ElfHeaderMipsFlags[] = {
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_NOREORDER),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_PIC),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_CPIC),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI2),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_32BITMODE),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_FP64),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_NAN2008),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI_O32),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI_O64),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI_EABI32),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI_EABI64),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_3900),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4010),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4100),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4650),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4120),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4111),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_SB1),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_OCTEON),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_XLR),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_OCTEON2),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_OCTEON3),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_5400),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_5900),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_5500),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_9000),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_LS2E),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_LS2F),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_LS3A),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MICROMIPS),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_ASE_M16),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_ASE_MDMX),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_1),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_2),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_3),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_4),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_5),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_32),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_64),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_32R2),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_64R2),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_32R6),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_64R6)
+ ENUM_ENT(EF_MIPS_NOREORDER, "noreorder"),
+ ENUM_ENT(EF_MIPS_PIC, "pic"),
+ ENUM_ENT(EF_MIPS_CPIC, "cpic"),
+ ENUM_ENT(EF_MIPS_ABI2, "abi2"),
+ ENUM_ENT(EF_MIPS_32BITMODE, "32bitmode"),
+ ENUM_ENT(EF_MIPS_FP64, "fp64"),
+ ENUM_ENT(EF_MIPS_NAN2008, "nan2008"),
+ ENUM_ENT(EF_MIPS_ABI_O32, "o32"),
+ ENUM_ENT(EF_MIPS_ABI_O64, "o64"),
+ ENUM_ENT(EF_MIPS_ABI_EABI32, "eabi32"),
+ ENUM_ENT(EF_MIPS_ABI_EABI64, "eabi64"),
+ ENUM_ENT(EF_MIPS_MACH_3900, "3900"),
+ ENUM_ENT(EF_MIPS_MACH_4010, "4010"),
+ ENUM_ENT(EF_MIPS_MACH_4100, "4100"),
+ ENUM_ENT(EF_MIPS_MACH_4650, "4650"),
+ ENUM_ENT(EF_MIPS_MACH_4120, "4120"),
+ ENUM_ENT(EF_MIPS_MACH_4111, "4111"),
+ ENUM_ENT(EF_MIPS_MACH_SB1, "sb1"),
+ ENUM_ENT(EF_MIPS_MACH_OCTEON, "octeon"),
+ ENUM_ENT(EF_MIPS_MACH_XLR, "xlr"),
+ ENUM_ENT(EF_MIPS_MACH_OCTEON2, "octeon2"),
+ ENUM_ENT(EF_MIPS_MACH_OCTEON3, "octeon3"),
+ ENUM_ENT(EF_MIPS_MACH_5400, "5400"),
+ ENUM_ENT(EF_MIPS_MACH_5900, "5900"),
+ ENUM_ENT(EF_MIPS_MACH_5500, "5500"),
+ ENUM_ENT(EF_MIPS_MACH_9000, "9000"),
+ ENUM_ENT(EF_MIPS_MACH_LS2E, "loongson-2e"),
+ ENUM_ENT(EF_MIPS_MACH_LS2F, "loongson-2f"),
+ ENUM_ENT(EF_MIPS_MACH_LS3A, "loongson-3a"),
+ ENUM_ENT(EF_MIPS_MICROMIPS, "micromips"),
+ ENUM_ENT(EF_MIPS_ARCH_ASE_M16, "mips16"),
+ ENUM_ENT(EF_MIPS_ARCH_ASE_MDMX, "mdmx"),
+ ENUM_ENT(EF_MIPS_ARCH_1, "mips1"),
+ ENUM_ENT(EF_MIPS_ARCH_2, "mips2"),
+ ENUM_ENT(EF_MIPS_ARCH_3, "mips3"),
+ ENUM_ENT(EF_MIPS_ARCH_4, "mips4"),
+ ENUM_ENT(EF_MIPS_ARCH_5, "mips5"),
+ ENUM_ENT(EF_MIPS_ARCH_32, "mips32"),
+ ENUM_ENT(EF_MIPS_ARCH_64, "mips64"),
+ ENUM_ENT(EF_MIPS_ARCH_32R2, "mips32r2"),
+ ENUM_ENT(EF_MIPS_ARCH_64R2, "mips64r2"),
+ ENUM_ENT(EF_MIPS_ARCH_32R6, "mips32r6"),
+ ENUM_ENT(EF_MIPS_ARCH_64R6, "mips64r6")
};
static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = {
@@ -1325,15 +1368,17 @@ static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = {
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX902),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX904),
LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX906),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_XNACK)
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX909),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_XNACK),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_SRAM_ECC)
};
static const EnumEntry<unsigned> ElfHeaderRISCVFlags[] = {
- LLVM_READOBJ_ENUM_ENT(ELF, EF_RISCV_RVC),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_RISCV_FLOAT_ABI_SINGLE),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_RISCV_FLOAT_ABI_DOUBLE),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_RISCV_FLOAT_ABI_QUAD),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_RISCV_RVE)
+ ENUM_ENT(EF_RISCV_RVC, "RVC"),
+ ENUM_ENT(EF_RISCV_FLOAT_ABI_SINGLE, "single-float ABI"),
+ ENUM_ENT(EF_RISCV_FLOAT_ABI_DOUBLE, "double-float ABI"),
+ ENUM_ENT(EF_RISCV_FLOAT_ABI_QUAD, "quad-float ABI"),
+ ENUM_ENT(EF_RISCV_RVE, "RVE")
};
static const EnumEntry<unsigned> ElfSymOtherFlags[] = {
@@ -1375,9 +1420,11 @@ static const char *getElfMipsOptionsOdkType(unsigned Odk) {
}
template <typename ELFT>
-ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, ScopedPrinter &Writer)
- : ObjDumper(Writer), Obj(Obj) {
+ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF,
+ ScopedPrinter &Writer)
+ : ObjDumper(Writer), ObjF(ObjF) {
SmallVector<const Elf_Phdr *, 4> LoadSegments;
+ const ELFFile<ELFT> *Obj = ObjF->getELFFile();
for (const Elf_Phdr &Phdr : unwrapOrError(Obj->program_headers())) {
if (Phdr.p_type == ELF::PT_DYNAMIC) {
DynamicTable = createDRIFrom(&Phdr, sizeof(Elf_Dyn));
@@ -1423,7 +1470,7 @@ ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, ScopedPrinter &Writer)
break;
case ELF::SHT_LLVM_CALL_GRAPH_PROFILE:
if (DotCGProfileSec != nullptr)
- reportError("Multiple .note.llvm.cgprofile");
+ reportError("Multiple .llvm.call-graph-profile");
DotCGProfileSec = &Sec;
break;
case ELF::SHT_LLVM_ADDRSIG:
@@ -1446,19 +1493,10 @@ template <typename ELFT>
void ELFDumper<ELFT>::parseDynamicTable(
ArrayRef<const Elf_Phdr *> LoadSegments) {
auto toMappedAddr = [&](uint64_t VAddr) -> const uint8_t * {
- const Elf_Phdr *const *I =
- std::upper_bound(LoadSegments.begin(), LoadSegments.end(), VAddr,
- [](uint64_t VAddr, const Elf_Phdr_Impl<ELFT> *Phdr) {
- return VAddr < Phdr->p_vaddr;
- });
- if (I == LoadSegments.begin())
- report_fatal_error("Virtual address is not in any segment");
- --I;
- const Elf_Phdr &Phdr = **I;
- uint64_t Delta = VAddr - Phdr.p_vaddr;
- if (Delta >= Phdr.p_filesz)
- report_fatal_error("Virtual address is not in any segment");
- return Obj->base() + Phdr.p_offset + Delta;
+ auto MappedAddrOrError = ObjF->getELFFile()->toMappedAddr(VAddr);
+ if (!MappedAddrOrError)
+ report_fatal_error(MappedAddrOrError.takeError());
+ return MappedAddrOrError.get();
};
uint64_t SONameOffset = 0;
@@ -1557,51 +1595,51 @@ typename ELFDumper<ELFT>::Elf_Relr_Range ELFDumper<ELFT>::dyn_relrs() const {
template<class ELFT>
void ELFDumper<ELFT>::printFileHeaders() {
- ELFDumperStyle->printFileHeaders(Obj);
+ ELFDumperStyle->printFileHeaders(ObjF->getELFFile());
}
template<class ELFT>
-void ELFDumper<ELFT>::printSections() {
- ELFDumperStyle->printSections(Obj);
+void ELFDumper<ELFT>::printSectionHeaders() {
+ ELFDumperStyle->printSectionHeaders(ObjF->getELFFile());
}
template<class ELFT>
void ELFDumper<ELFT>::printRelocations() {
- ELFDumperStyle->printRelocations(Obj);
+ ELFDumperStyle->printRelocations(ObjF->getELFFile());
}
template <class ELFT> void ELFDumper<ELFT>::printProgramHeaders() {
- ELFDumperStyle->printProgramHeaders(Obj);
+ ELFDumperStyle->printProgramHeaders(ObjF->getELFFile());
}
template <class ELFT> void ELFDumper<ELFT>::printDynamicRelocations() {
- ELFDumperStyle->printDynamicRelocations(Obj);
+ ELFDumperStyle->printDynamicRelocations(ObjF->getELFFile());
}
template<class ELFT>
void ELFDumper<ELFT>::printSymbols() {
- ELFDumperStyle->printSymbols(Obj);
+ ELFDumperStyle->printSymbols(ObjF->getELFFile());
}
template<class ELFT>
void ELFDumper<ELFT>::printDynamicSymbols() {
- ELFDumperStyle->printDynamicSymbols(Obj);
+ ELFDumperStyle->printDynamicSymbols(ObjF->getELFFile());
}
template <class ELFT> void ELFDumper<ELFT>::printHashHistogram() {
- ELFDumperStyle->printHashHistogram(Obj);
+ ELFDumperStyle->printHashHistogram(ObjF->getELFFile());
}
template <class ELFT> void ELFDumper<ELFT>::printCGProfile() {
- ELFDumperStyle->printCGProfile(Obj);
+ ELFDumperStyle->printCGProfile(ObjF->getELFFile());
}
template <class ELFT> void ELFDumper<ELFT>::printNotes() {
- ELFDumperStyle->printNotes(Obj);
+ ELFDumperStyle->printNotes(ObjF->getELFFile());
}
template <class ELFT> void ELFDumper<ELFT>::printELFLinkerOptions() {
- ELFDumperStyle->printELFLinkerOptions(Obj);
+ ELFDumperStyle->printELFLinkerOptions(ObjF->getELFFile());
}
static const char *getTypeString(unsigned Arch, uint64_t Type) {
@@ -1610,29 +1648,32 @@ static const char *getTypeString(unsigned Arch, uint64_t Type) {
case EM_HEXAGON:
switch (Type) {
#define HEXAGON_DYNAMIC_TAG(name, value) \
- case DT_##name: \
- return #name;
+ case DT_##name: \
+ return #name;
#include "llvm/BinaryFormat/DynamicTags.def"
#undef HEXAGON_DYNAMIC_TAG
}
+ break;
case EM_MIPS:
switch (Type) {
#define MIPS_DYNAMIC_TAG(name, value) \
- case DT_##name: \
- return #name;
+ case DT_##name: \
+ return #name;
#include "llvm/BinaryFormat/DynamicTags.def"
#undef MIPS_DYNAMIC_TAG
}
+ break;
- case EM_PPC64:
- switch(Type) {
+ case EM_PPC64:
+ switch(Type) {
#define PPC64_DYNAMIC_TAG(name, value) \
case DT_##name: \
return #name;
#include "llvm/BinaryFormat/DynamicTags.def"
#undef PPC64_DYNAMIC_TAG
}
+ break;
}
#undef DYNAMIC_TAG
switch (Type) {
@@ -1842,9 +1883,9 @@ void ELFDumper<ELFT>::printValue(uint64_t Type, uint64_t Value) {
template<class ELFT>
void ELFDumper<ELFT>::printUnwindInfo() {
- const unsigned Machine = Obj->getHeader()->e_machine;
+ const unsigned Machine = ObjF->getELFFile()->getHeader()->e_machine;
if (Machine == EM_386 || Machine == EM_X86_64) {
- DwarfCFIEH::PrinterContext<ELFT> Ctx(W, Obj);
+ DwarfCFIEH::PrinterContext<ELFT> Ctx(W, ObjF);
return Ctx.printUnwindInformation();
}
W.startLine() << "UnwindInfo not implemented.\n";
@@ -1853,6 +1894,7 @@ void ELFDumper<ELFT>::printUnwindInfo() {
namespace {
template <> void ELFDumper<ELF32LE>::printUnwindInfo() {
+ const ELFFile<ELF32LE> *Obj = ObjF->getELFFile();
const unsigned Machine = Obj->getHeader()->e_machine;
if (Machine == EM_ARM) {
ARM::EHABI::PrinterContext<ELF32LE> Ctx(W, Obj, DotSymtabSec);
@@ -1895,7 +1937,7 @@ void ELFDumper<ELFT>::printDynamicTable() {
uintX_t Tag = Entry.getTag();
++I;
W.startLine() << " " << format_hex(Tag, Is64 ? 18 : 10, opts::Output != opts::GNU) << " "
- << format("%-21s", getTypeString(Obj->getHeader()->e_machine, Tag));
+ << format("%-21s", getTypeString(ObjF->getELFFile()->getHeader()->e_machine, Tag));
printValue(Tag, Entry.getVal());
OS << "\n";
}
@@ -1962,6 +2004,7 @@ void ELFDumper<ELFT>::printAttributes() {
namespace {
template <> void ELFDumper<ELF32LE>::printAttributes() {
+ const ELFFile<ELF32LE> *Obj = ObjF->getELFFile();
if (Obj->getHeader()->e_machine != EM_ARM) {
W.startLine() << "Attributes not implemented.\n";
return;
@@ -2247,6 +2290,7 @@ MipsGOTParser<ELFT>::getPltSym(const Entry *E) const {
}
template <class ELFT> void ELFDumper<ELFT>::printMipsPLTGOT() {
+ const ELFFile<ELFT> *Obj = ObjF->getELFFile();
if (Obj->getHeader()->e_machine != EM_MIPS)
reportError("MIPS PLT GOT is available for MIPS targets only");
@@ -2331,6 +2375,7 @@ static int getMipsRegisterSize(uint8_t Flag) {
}
template <class ELFT> void ELFDumper<ELFT>::printMipsABIFlags() {
+ const ELFFile<ELFT> *Obj = ObjF->getELFFile();
const Elf_Shdr *Shdr = findSectionByName(*Obj, ".MIPS.abiflags");
if (!Shdr) {
W.startLine() << "There is no .MIPS.abiflags section in the file.\n";
@@ -2376,6 +2421,7 @@ static void printMipsReginfoData(ScopedPrinter &W,
}
template <class ELFT> void ELFDumper<ELFT>::printMipsReginfo() {
+ const ELFFile<ELFT> *Obj = ObjF->getELFFile();
const Elf_Shdr *Shdr = findSectionByName(*Obj, ".reginfo");
if (!Shdr) {
W.startLine() << "There is no .reginfo section in the file.\n";
@@ -2393,6 +2439,7 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsReginfo() {
}
template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() {
+ const ELFFile<ELFT> *Obj = ObjF->getELFFile();
const Elf_Shdr *Shdr = findSectionByName(*Obj, ".MIPS.options");
if (!Shdr) {
W.startLine() << "There is no .MIPS.options section in the file.\n";
@@ -2422,6 +2469,7 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() {
}
template <class ELFT> void ELFDumper<ELFT>::printStackMap() const {
+ const ELFFile<ELFT> *Obj = ObjF->getELFFile();
const Elf_Shdr *StackMapSection = nullptr;
for (const auto &Sec : unwrapOrError(Obj->sections())) {
StringRef Name = unwrapOrError(Obj->getSectionName(&Sec));
@@ -2442,11 +2490,11 @@ template <class ELFT> void ELFDumper<ELFT>::printStackMap() const {
}
template <class ELFT> void ELFDumper<ELFT>::printGroupSections() {
- ELFDumperStyle->printGroupSections(Obj);
+ ELFDumperStyle->printGroupSections(ObjF->getELFFile());
}
template <class ELFT> void ELFDumper<ELFT>::printAddrsig() {
- ELFDumperStyle->printAddrsig(Obj);
+ ELFDumperStyle->printAddrsig(ObjF->getELFFile());
}
static inline void printFields(formatted_raw_ostream &OS, StringRef Str1,
@@ -2517,7 +2565,17 @@ template <class ELFT> void GNUStyle<ELFT>::printFileHeaders(const ELFO *Obj) {
printFields(OS, "Start of program headers:", Str);
Str = to_string(e->e_shoff) + " (bytes into file)";
printFields(OS, "Start of section headers:", Str);
+ std::string ElfFlags;
+ if (e->e_machine == EM_MIPS)
+ ElfFlags =
+ printFlags(e->e_flags, makeArrayRef(ElfHeaderMipsFlags),
+ unsigned(ELF::EF_MIPS_ARCH), unsigned(ELF::EF_MIPS_ABI),
+ unsigned(ELF::EF_MIPS_MACH));
+ else if (e->e_machine == EM_RISCV)
+ ElfFlags = printFlags(e->e_flags, makeArrayRef(ElfHeaderRISCVFlags));
Str = "0x" + to_hexString(e->e_flags);
+ if (!ElfFlags.empty())
+ Str = Str + ", " + ElfFlags;
printFields(OS, "Flags:", Str);
Str = to_string(e->e_ehsize) + " (bytes)";
printFields(OS, "Size of this header:", Str);
@@ -2791,11 +2849,13 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) {
case SHT_ARM_OVERLAYSECTION:
return "ARM_OVERLAYSECTION";
}
+ break;
case EM_X86_64:
switch (Type) {
case SHT_X86_64_UNWIND:
return "X86_64_UNWIND";
}
+ break;
case EM_MIPS:
case EM_MIPS_RS3_LE:
switch (Type) {
@@ -2808,6 +2868,7 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) {
case SHT_MIPS_DWARF:
return "SHT_MIPS_DWARF";
}
+ break;
}
switch (Type) {
case SHT_NULL:
@@ -2872,7 +2933,8 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) {
return "";
}
-template <class ELFT> void GNUStyle<ELFT>::printSections(const ELFO *Obj) {
+template <class ELFT>
+void GNUStyle<ELFT>::printSectionHeaders(const ELFO *Obj) {
size_t SectionIndex = 0;
std::string Number, Type, Size, Address, Offset, Flags, Link, Info, EntrySize,
Alignment;
@@ -3583,7 +3645,7 @@ static std::string getFreeBSDNoteTypeName(const uint32_t NT) {
return OS.str();
}
-static std::string getAMDGPUNoteTypeName(const uint32_t NT) {
+static std::string getAMDNoteTypeName(const uint32_t NT) {
static const struct {
uint32_t ID;
const char *Name;
@@ -3606,41 +3668,52 @@ static std::string getAMDGPUNoteTypeName(const uint32_t NT) {
return OS.str();
}
+static std::string getAMDGPUNoteTypeName(const uint32_t NT) {
+ if (NT == ELF::NT_AMDGPU_METADATA)
+ return std::string("NT_AMDGPU_METADATA (AMDGPU Metadata)");
+
+ std::string string;
+ raw_string_ostream OS(string);
+ OS << format("Unknown note type (0x%08x)", NT);
+ return OS.str();
+}
+
template <typename ELFT>
-static void printGNUProperty(raw_ostream &OS, uint32_t Type, uint32_t DataSize,
- ArrayRef<uint8_t> Data) {
+static std::string getGNUProperty(uint32_t Type, uint32_t DataSize,
+ ArrayRef<uint8_t> Data) {
+ std::string str;
+ raw_string_ostream OS(str);
switch (Type) {
default:
- OS << format(" <application-specific type 0x%x>\n", Type);
- return;
+ OS << format("<application-specific type 0x%x>", Type);
+ return OS.str();
case GNU_PROPERTY_STACK_SIZE: {
- OS << " stack size: ";
+ OS << "stack size: ";
if (DataSize == sizeof(typename ELFT::uint))
- OS << format("0x%llx\n",
- (uint64_t)(*(const typename ELFT::Addr *)Data.data()));
+ OS << formatv("{0:x}",
+ (uint64_t)(*(const typename ELFT::Addr *)Data.data()));
else
- OS << format("<corrupt length: 0x%x>\n", DataSize);
- break;
+ OS << format("<corrupt length: 0x%x>", DataSize);
+ return OS.str();
}
case GNU_PROPERTY_NO_COPY_ON_PROTECTED:
- OS << " no copy on protected";
+ OS << "no copy on protected";
if (DataSize)
OS << format(" <corrupt length: 0x%x>", DataSize);
- OS << "\n";
- break;
+ return OS.str();
case GNU_PROPERTY_X86_FEATURE_1_AND:
- OS << " X86 features: ";
+ OS << "X86 features: ";
if (DataSize != 4 && DataSize != 8) {
- OS << format("<corrupt length: 0x%x>\n", DataSize);
- break;
+ OS << format("<corrupt length: 0x%x>", DataSize);
+ return OS.str();
}
uint64_t CFProtection =
(DataSize == 4)
? support::endian::read32<ELFT::TargetEndianness>(Data.data())
: support::endian::read64<ELFT::TargetEndianness>(Data.data());
if (CFProtection == 0) {
- OS << "none\n";
- break;
+ OS << "none";
+ return OS.str();
}
if (CFProtection & GNU_PROPERTY_X86_FEATURE_1_IBT) {
OS << "IBT";
@@ -3656,105 +3729,177 @@ static void printGNUProperty(raw_ostream &OS, uint32_t Type, uint32_t DataSize,
}
if (CFProtection)
OS << format("<unknown flags: 0x%llx>", CFProtection);
- OS << "\n";
- break;
+ return OS.str();
}
}
template <typename ELFT>
-static void printGNUNote(raw_ostream &OS, uint32_t NoteType,
- ArrayRef<typename ELFT::Word> Words, size_t Size) {
+static SmallVector<std::string, 4>
+getGNUPropertyList(ArrayRef<uint8_t> Arr) {
using Elf_Word = typename ELFT::Word;
+ SmallVector<std::string, 4> Properties;
+ while (Arr.size() >= 8) {
+ uint32_t Type = *reinterpret_cast<const Elf_Word *>(Arr.data());
+ uint32_t DataSize = *reinterpret_cast<const Elf_Word *>(Arr.data() + 4);
+ Arr = Arr.drop_front(8);
+
+ // Take padding size into account if present.
+ uint64_t PaddedSize = alignTo(DataSize, sizeof(typename ELFT::uint));
+ std::string str;
+ raw_string_ostream OS(str);
+ if (Arr.size() < PaddedSize) {
+ OS << format("<corrupt type (0x%x) datasz: 0x%x>", Type, DataSize);
+ Properties.push_back(OS.str());
+ break;
+ }
+ Properties.push_back(
+ getGNUProperty<ELFT>(Type, DataSize, Arr.take_front(PaddedSize)));
+ Arr = Arr.drop_front(PaddedSize);
+ }
+
+ if (!Arr.empty())
+ Properties.push_back("<corrupted GNU_PROPERTY_TYPE_0>");
+
+ return Properties;
+}
+
+struct GNUAbiTag {
+ std::string OSName;
+ std::string ABI;
+ bool IsValid;
+};
+
+template <typename ELFT>
+static GNUAbiTag getGNUAbiTag(ArrayRef<uint8_t> Desc) {
+ typedef typename ELFT::Word Elf_Word;
+
+ ArrayRef<Elf_Word> Words(reinterpret_cast<const Elf_Word*>(Desc.begin()),
+ reinterpret_cast<const Elf_Word*>(Desc.end()));
+
+ if (Words.size() < 4)
+ return {"", "", /*IsValid=*/false};
+
+ static const char *OSNames[] = {
+ "Linux", "Hurd", "Solaris", "FreeBSD", "NetBSD", "Syllable", "NaCl",
+ };
+ StringRef OSName = "Unknown";
+ if (Words[0] < array_lengthof(OSNames))
+ OSName = OSNames[Words[0]];
+ uint32_t Major = Words[1], Minor = Words[2], Patch = Words[3];
+ std::string str;
+ raw_string_ostream ABI(str);
+ ABI << Major << "." << Minor << "." << Patch;
+ return {OSName, ABI.str(), /*IsValid=*/true};
+}
+
+static std::string getGNUBuildId(ArrayRef<uint8_t> Desc) {
+ std::string str;
+ raw_string_ostream OS(str);
+ for (const auto &B : Desc)
+ OS << format_hex_no_prefix(B, 2);
+ return OS.str();
+}
+
+static StringRef getGNUGoldVersion(ArrayRef<uint8_t> Desc) {
+ return StringRef(reinterpret_cast<const char *>(Desc.data()), Desc.size());
+}
+
+template <typename ELFT>
+static void printGNUNote(raw_ostream &OS, uint32_t NoteType,
+ ArrayRef<uint8_t> Desc) {
switch (NoteType) {
default:
return;
case ELF::NT_GNU_ABI_TAG: {
- static const char *OSNames[] = {
- "Linux", "Hurd", "Solaris", "FreeBSD", "NetBSD", "Syllable", "NaCl",
- };
-
- StringRef OSName = "Unknown";
- if (Words[0] < array_lengthof(OSNames))
- OSName = OSNames[Words[0]];
- uint32_t Major = Words[1], Minor = Words[2], Patch = Words[3];
-
- if (Words.size() < 4)
+ const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc);
+ if (!AbiTag.IsValid)
OS << " <corrupt GNU_ABI_TAG>";
else
- OS << " OS: " << OSName << ", ABI: " << Major << "." << Minor << "."
- << Patch;
+ OS << " OS: " << AbiTag.OSName << ", ABI: " << AbiTag.ABI;
break;
}
case ELF::NT_GNU_BUILD_ID: {
- OS << " Build ID: ";
- ArrayRef<uint8_t> ID(reinterpret_cast<const uint8_t *>(Words.data()), Size);
- for (const auto &B : ID)
- OS << format_hex_no_prefix(B, 2);
+ OS << " Build ID: " << getGNUBuildId(Desc);
break;
}
case ELF::NT_GNU_GOLD_VERSION:
- OS << " Version: "
- << StringRef(reinterpret_cast<const char *>(Words.data()), Size);
+ OS << " Version: " << getGNUGoldVersion(Desc);
break;
case ELF::NT_GNU_PROPERTY_TYPE_0:
OS << " Properties:";
-
- ArrayRef<uint8_t> Arr(reinterpret_cast<const uint8_t *>(Words.data()),
- Size);
- while (Arr.size() >= 8) {
- uint32_t Type = *reinterpret_cast<const Elf_Word *>(Arr.data());
- uint32_t DataSize = *reinterpret_cast<const Elf_Word *>(Arr.data() + 4);
- Arr = Arr.drop_front(8);
-
- // Take padding size into account if present.
- uint64_t PaddedSize = alignTo(DataSize, sizeof(typename ELFT::uint));
- if (Arr.size() < PaddedSize) {
- OS << format(" <corrupt type (0x%x) datasz: 0x%x>\n", Type,
- DataSize);
- break;
- }
- printGNUProperty<ELFT>(OS, Type, DataSize, Arr.take_front(PaddedSize));
- Arr = Arr.drop_front(PaddedSize);
- }
-
- if (!Arr.empty())
- OS << " <corrupted GNU_PROPERTY_TYPE_0>";
+ for (const auto &Property : getGNUPropertyList<ELFT>(Desc))
+ OS << " " << Property << "\n";
break;
}
OS << '\n';
}
+struct AMDNote {
+ std::string Type;
+ std::string Value;
+};
+
template <typename ELFT>
-static void printAMDGPUNote(raw_ostream &OS, uint32_t NoteType,
- ArrayRef<typename ELFT::Word> Words, size_t Size) {
+static AMDNote getAMDNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) {
switch (NoteType) {
default:
- return;
- case ELF::NT_AMD_AMDGPU_HSA_METADATA:
- OS << " HSA Metadata:\n"
- << StringRef(reinterpret_cast<const char *>(Words.data()), Size);
- break;
- case ELF::NT_AMD_AMDGPU_ISA:
- OS << " ISA Version:\n"
- << " "
- << StringRef(reinterpret_cast<const char *>(Words.data()), Size);
- break;
- case ELF::NT_AMD_AMDGPU_PAL_METADATA:
- const uint32_t *PALMetadataBegin = reinterpret_cast<const uint32_t *>(Words.data());
- const uint32_t *PALMetadataEnd = PALMetadataBegin + Size;
- std::vector<uint32_t> PALMetadata(PALMetadataBegin, PALMetadataEnd);
- std::string PALMetadataString;
- auto Error = AMDGPU::PALMD::toString(PALMetadata, PALMetadataString);
- OS << " PAL Metadata:\n";
- if (Error) {
- OS << " Invalid";
- return;
- }
- OS << PALMetadataString;
- break;
+ return {"", ""};
+ case ELF::NT_AMD_AMDGPU_HSA_METADATA:
+ return {"HSA Metadata",
+ std::string(reinterpret_cast<const char *>(Desc.data()),
+ Desc.size())};
+ case ELF::NT_AMD_AMDGPU_ISA:
+ return {"ISA Version",
+ std::string(reinterpret_cast<const char *>(Desc.data()),
+ Desc.size())};
+ case ELF::NT_AMD_AMDGPU_PAL_METADATA:
+ const uint32_t *PALMetadataBegin =
+ reinterpret_cast<const uint32_t *>(Desc.data());
+ const uint32_t *PALMetadataEnd = PALMetadataBegin + Desc.size();
+ std::vector<uint32_t> PALMetadata(PALMetadataBegin, PALMetadataEnd);
+ std::string PALMetadataString;
+ auto Error = AMDGPU::PALMD::toString(PALMetadata, PALMetadataString);
+ if (Error) {
+ return {"PAL Metadata", "Invalid"};
+ }
+ return {"PAL Metadata", PALMetadataString};
+ }
+}
+
+struct AMDGPUNote {
+ std::string Type;
+ std::string Value;
+};
+
+template <typename ELFT>
+static AMDGPUNote getAMDGPUNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) {
+ switch (NoteType) {
+ default:
+ return {"", ""};
+ case ELF::NT_AMDGPU_METADATA:
+ auto MsgPackString =
+ StringRef(reinterpret_cast<const char *>(Desc.data()), Desc.size());
+ msgpack::Reader MsgPackReader(MsgPackString);
+ auto OptMsgPackNodeOrErr = msgpack::Node::read(MsgPackReader);
+ if (errorToBool(OptMsgPackNodeOrErr.takeError()))
+ return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"};
+ auto &OptMsgPackNode = *OptMsgPackNodeOrErr;
+ if (!OptMsgPackNode)
+ return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"};
+ auto &MsgPackNode = *OptMsgPackNode;
+
+ AMDGPU::HSAMD::V3::MetadataVerifier Verifier(true);
+ if (!Verifier.verify(*MsgPackNode))
+ return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"};
+
+ std::string HSAMetadataString;
+ raw_string_ostream StrOS(HSAMetadataString);
+ yaml::Output YOut(StrOS);
+ YOut << MsgPackNode;
+
+ return {"AMDGPU Metadata", StrOS.str()};
}
- OS.flush();
}
template <class ELFT>
@@ -3771,7 +3916,7 @@ void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) {
auto ProcessNote = [&](const Elf_Note &Note) {
StringRef Name = Note.getName();
- ArrayRef<Elf_Word> Descriptor = Note.getDesc();
+ ArrayRef<uint8_t> Descriptor = Note.getDesc();
Elf_Word Type = Note.getType();
OS << " " << Name << std::string(22 - Name.size(), ' ')
@@ -3779,12 +3924,19 @@ void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) {
if (Name == "GNU") {
OS << getGNUNoteTypeName(Type) << '\n';
- printGNUNote<ELFT>(OS, Type, Descriptor, Descriptor.size());
+ printGNUNote<ELFT>(OS, Type, Descriptor);
} else if (Name == "FreeBSD") {
OS << getFreeBSDNoteTypeName(Type) << '\n';
} else if (Name == "AMD") {
+ OS << getAMDNoteTypeName(Type) << '\n';
+ const AMDNote N = getAMDNote<ELFT>(Type, Descriptor);
+ if (!N.Type.empty())
+ OS << " " << N.Type << ":\n " << N.Value << '\n';
+ } else if (Name == "AMDGPU") {
OS << getAMDGPUNoteTypeName(Type) << '\n';
- printAMDGPUNote<ELFT>(OS, Type, Descriptor, Descriptor.size());
+ const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor);
+ if (!N.Type.empty())
+ OS << " " << N.Type << ":\n " << N.Value << '\n';
} else {
OS << "Unknown note type: (" << format_hex(Type, 10) << ')';
}
@@ -4123,7 +4275,8 @@ void LLVMStyle<ELFT>::printRelocation(const ELFO *Obj, Elf_Rela Rel,
}
}
-template <class ELFT> void LLVMStyle<ELFT>::printSections(const ELFO *Obj) {
+template <class ELFT>
+void LLVMStyle<ELFT>::printSectionHeaders(const ELFO *Obj) {
ListScope SectionsD(W, "Sections");
int SectionIndex = -1;
@@ -4379,7 +4532,7 @@ void LLVMStyle<ELFT>::printAddrsig(const ELFFile<ELFT> *Obj) {
while (Cur != End) {
unsigned Size;
const char *Err;
- uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err);
+ uint64_t SymIndex = decodeULEB128(Cur, &Size, End, &Err);
if (Err)
reportError(Err);
W.printNumber("Sym", this->dumper()->getStaticSymbolName(SymIndex),
@@ -4388,9 +4541,103 @@ void LLVMStyle<ELFT>::printAddrsig(const ELFFile<ELFT> *Obj) {
}
}
+template <typename ELFT>
+static void printGNUNoteLLVMStyle(uint32_t NoteType,
+ ArrayRef<uint8_t> Desc,
+ ScopedPrinter &W) {
+ switch (NoteType) {
+ default:
+ return;
+ case ELF::NT_GNU_ABI_TAG: {
+ const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc);
+ if (!AbiTag.IsValid) {
+ W.printString("ABI", "<corrupt GNU_ABI_TAG>");
+ } else {
+ W.printString("OS", AbiTag.OSName);
+ W.printString("ABI", AbiTag.ABI);
+ }
+ break;
+ }
+ case ELF::NT_GNU_BUILD_ID: {
+ W.printString("Build ID", getGNUBuildId(Desc));
+ break;
+ }
+ case ELF::NT_GNU_GOLD_VERSION:
+ W.printString("Version", getGNUGoldVersion(Desc));
+ break;
+ case ELF::NT_GNU_PROPERTY_TYPE_0:
+ ListScope D(W, "Property");
+ for (const auto &Property : getGNUPropertyList<ELFT>(Desc))
+ W.printString(Property);
+ break;
+ }
+}
+
template <class ELFT>
void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) {
- W.startLine() << "printNotes not implemented!\n";
+ ListScope L(W, "Notes");
+ const Elf_Ehdr *e = Obj->getHeader();
+ bool IsCore = e->e_type == ELF::ET_CORE;
+
+ auto PrintHeader = [&](const typename ELFT::Off Offset,
+ const typename ELFT::Addr Size) {
+ W.printHex("Offset", Offset);
+ W.printHex("Size", Size);
+ };
+
+ auto ProcessNote = [&](const Elf_Note &Note) {
+ DictScope D2(W, "Note");
+ StringRef Name = Note.getName();
+ ArrayRef<uint8_t> Descriptor = Note.getDesc();
+ Elf_Word Type = Note.getType();
+
+ W.printString("Owner", Name);
+ W.printHex("Data size", Descriptor.size());
+ if (Name == "GNU") {
+ W.printString("Type", getGNUNoteTypeName(Type));
+ printGNUNoteLLVMStyle<ELFT>(Type, Descriptor, W);
+ } else if (Name == "FreeBSD") {
+ W.printString("Type", getFreeBSDNoteTypeName(Type));
+ } else if (Name == "AMD") {
+ W.printString("Type", getAMDNoteTypeName(Type));
+ const AMDNote N = getAMDNote<ELFT>(Type, Descriptor);
+ if (!N.Type.empty())
+ W.printString(N.Type, N.Value);
+ } else if (Name == "AMDGPU") {
+ W.printString("Type", getAMDGPUNoteTypeName(Type));
+ const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor);
+ if (!N.Type.empty())
+ W.printString(N.Type, N.Value);
+ } else {
+ W.getOStream() << "Unknown note type: (" << format_hex(Type, 10) << ')';
+ }
+ };
+
+ if (IsCore) {
+ for (const auto &P : unwrapOrError(Obj->program_headers())) {
+ if (P.p_type != PT_NOTE)
+ continue;
+ DictScope D(W, "NoteSection");
+ PrintHeader(P.p_offset, P.p_filesz);
+ Error Err = Error::success();
+ for (const auto &Note : Obj->notes(P, Err))
+ ProcessNote(Note);
+ if (Err)
+ error(std::move(Err));
+ }
+ } else {
+ for (const auto &S : unwrapOrError(Obj->sections())) {
+ if (S.sh_type != SHT_NOTE)
+ continue;
+ DictScope D(W, "NoteSection");
+ PrintHeader(S.sh_offset, S.sh_size);
+ Error Err = Error::success();
+ for (const auto &Note : Obj->notes(S, Err))
+ ProcessNote(Note);
+ if (Err)
+ error(std::move(Err));
+ }
+ }
}
template <class ELFT>
diff --git a/tools/llvm-readobj/MachODumper.cpp b/tools/llvm-readobj/MachODumper.cpp
index 69ef1556f78d..35e4cfcb6b10 100644
--- a/tools/llvm-readobj/MachODumper.cpp
+++ b/tools/llvm-readobj/MachODumper.cpp
@@ -32,7 +32,7 @@ public:
: ObjDumper(Writer), Obj(Obj) {}
void printFileHeaders() override;
- void printSections() override;
+ void printSectionHeaders() override;
void printRelocations() override;
void printSymbols() override;
void printDynamicSymbols() override;
@@ -59,7 +59,7 @@ private:
void printRelocation(const MachOObjectFile *Obj, const RelocationRef &Reloc);
- void printSections(const MachOObjectFile *Obj);
+ void printSectionHeaders(const MachOObjectFile *Obj);
const MachOObjectFile *Obj;
};
@@ -428,11 +428,9 @@ void MachODumper::printFileHeaders(const MachHeader &Header) {
W.printFlags("Flags", Header.flags, makeArrayRef(MachOHeaderFlags));
}
-void MachODumper::printSections() {
- return printSections(Obj);
-}
+void MachODumper::printSectionHeaders() { return printSectionHeaders(Obj); }
-void MachODumper::printSections(const MachOObjectFile *Obj) {
+void MachODumper::printSectionHeaders(const MachOObjectFile *Obj) {
ListScope Group(W, "Sections");
int SectionIndex = -1;
diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h
index 8c3a7bec73be..13de563469ab 100644
--- a/tools/llvm-readobj/ObjDumper.h
+++ b/tools/llvm-readobj/ObjDumper.h
@@ -33,7 +33,7 @@ public:
virtual ~ObjDumper();
virtual void printFileHeaders() = 0;
- virtual void printSections() = 0;
+ virtual void printSectionHeaders() = 0;
virtual void printRelocations() = 0;
virtual void printSymbols() = 0;
virtual void printDynamicSymbols() = 0;
diff --git a/tools/llvm-readobj/WasmDumper.cpp b/tools/llvm-readobj/WasmDumper.cpp
index ce224836225e..79d3db4e2d29 100644
--- a/tools/llvm-readobj/WasmDumper.cpp
+++ b/tools/llvm-readobj/WasmDumper.cpp
@@ -23,28 +23,21 @@ using namespace object;
namespace {
static const EnumEntry<unsigned> WasmSymbolTypes[] = {
-#define ENUM_ENTRY(X) { #X, wasm::WASM_SYMBOL_TYPE_##X }
- ENUM_ENTRY(FUNCTION),
- ENUM_ENTRY(DATA),
- ENUM_ENTRY(GLOBAL),
- ENUM_ENTRY(SECTION),
+#define ENUM_ENTRY(X) \
+ { #X, wasm::WASM_SYMBOL_TYPE_##X }
+ ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL),
+ ENUM_ENTRY(SECTION), ENUM_ENTRY(EVENT),
#undef ENUM_ENTRY
};
static const EnumEntry<uint32_t> WasmSectionTypes[] = {
-#define ENUM_ENTRY(X) { #X, wasm::WASM_SEC_##X }
- ENUM_ENTRY(CUSTOM),
- ENUM_ENTRY(TYPE),
- ENUM_ENTRY(IMPORT),
- ENUM_ENTRY(FUNCTION),
- ENUM_ENTRY(TABLE),
- ENUM_ENTRY(MEMORY),
- ENUM_ENTRY(GLOBAL),
- ENUM_ENTRY(EXPORT),
- ENUM_ENTRY(START),
- ENUM_ENTRY(ELEM),
- ENUM_ENTRY(CODE),
- ENUM_ENTRY(DATA),
+#define ENUM_ENTRY(X) \
+ { #X, wasm::WASM_SEC_##X }
+ ENUM_ENTRY(CUSTOM), ENUM_ENTRY(TYPE), ENUM_ENTRY(IMPORT),
+ ENUM_ENTRY(FUNCTION), ENUM_ENTRY(TABLE), ENUM_ENTRY(MEMORY),
+ ENUM_ENTRY(GLOBAL), ENUM_ENTRY(EVENT), ENUM_ENTRY(EXPORT),
+ ENUM_ENTRY(START), ENUM_ENTRY(ELEM), ENUM_ENTRY(CODE),
+ ENUM_ENTRY(DATA),
#undef ENUM_ENTRY
};
@@ -54,7 +47,7 @@ public:
: ObjDumper(Writer), Obj(Obj) {}
void printFileHeaders() override;
- void printSections() override;
+ void printSectionHeaders() override;
void printRelocations() override;
void printSymbols() override;
void printDynamicSymbols() override { llvm_unreachable("unimplemented"); }
@@ -108,7 +101,7 @@ void WasmDumper::printRelocation(const SectionRef &Section,
if (HasAddend)
W.printNumber("Addend", WasmReloc.Addend);
} else {
- raw_ostream& OS = W.startLine();
+ raw_ostream &OS = W.startLine();
OS << W.hex(Reloc.getOffset()) << " " << RelocTypeName << " ";
if (!SymName.empty())
OS << SymName;
@@ -154,7 +147,7 @@ void WasmDumper::printSymbols() {
printSymbol(Symbol);
}
-void WasmDumper::printSections() {
+void WasmDumper::printSectionHeaders() {
ListScope Group(W, "Sections");
for (const SectionRef &Section : Obj->sections()) {
const WasmSection &WasmSec = Obj->getWasmSection(Section);
@@ -169,7 +162,7 @@ void WasmDumper::printSections() {
const wasm::WasmLinkingData &LinkingData = Obj->linkingData();
if (!LinkingData.InitFunctions.empty()) {
ListScope Group(W, "InitFunctions");
- for (const wasm::WasmInitFunc &F: LinkingData.InitFunctions)
+ for (const wasm::WasmInitFunc &F : LinkingData.InitFunctions)
W.startLine() << F.Symbol << " (priority=" << F.Priority << ")\n";
}
}
@@ -177,7 +170,7 @@ void WasmDumper::printSections() {
case wasm::WASM_SEC_DATA: {
ListScope Group(W, "Segments");
for (const WasmSegment &Segment : Obj->dataSegments()) {
- const wasm::WasmDataSegment& Seg = Segment.Data;
+ const wasm::WasmDataSegment &Seg = Segment.Data;
DictScope Group(W, "Segment");
if (!Seg.Name.empty())
W.printString("Name", Seg.Name);
@@ -219,7 +212,7 @@ void WasmDumper::printSymbol(const SymbolRef &Sym) {
W.printHex("Flags", Symbol.Info.Flags);
}
-}
+} // namespace
namespace llvm {
diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp
index a7236c02b8ae..81ce7a590364 100644
--- a/tools/llvm-readobj/llvm-readobj.cpp
+++ b/tools/llvm-readobj/llvm-readobj.cpp
@@ -48,58 +48,72 @@ namespace opts {
cl::desc("<input object files>"),
cl::ZeroOrMore);
+ // -all, -a
+ cl::opt<bool>
+ All("all",
+ cl::desc("Equivalent to setting: --file-headers, --program-headers, "
+ "--section-headers, --symbols, --relocations, "
+ "--dynamic-table, --notes, --version-info, --unwind, "
+ "--section-groups and --elf-hash-histogram."));
+ cl::alias AllShort("a", cl::desc("Alias for --all"), cl::aliasopt(All));
+
+ // --headers -e
+ cl::opt<bool>
+ Headers("headers",
+ cl::desc("Equivalent to setting: --file-headers, --program-headers, "
+ "--section-headers"));
+ cl::alias HeadersShort("e", cl::desc("Alias for --headers"),
+ cl::aliasopt(Headers));
+
// -wide, -W
- cl::opt<bool> WideOutput("wide",
- cl::desc("Ignored for compatibility with GNU readelf"));
+ cl::opt<bool>
+ WideOutput("wide", cl::desc("Ignored for compatibility with GNU readelf"),
+ cl::Hidden);
cl::alias WideOutputShort("W",
cl::desc("Alias for --wide"),
cl::aliasopt(WideOutput));
- // -file-headers, -h
+ // -file-headers, -file-header, -h
cl::opt<bool> FileHeaders("file-headers",
cl::desc("Display file headers "));
- cl::alias FileHeadersShort("h",
- cl::desc("Alias for --file-headers"),
- cl::aliasopt(FileHeaders));
-
- // -sections, -s, -S
- // Note: In GNU readelf, -s means --symbols!
- cl::opt<bool> Sections("sections",
- cl::desc("Display all sections."));
- cl::alias SectionsShort("s",
- cl::desc("Alias for --sections"),
- cl::aliasopt(Sections));
- cl::alias SectionsShortUpper("S",
- cl::desc("Alias for --sections"),
- cl::aliasopt(Sections));
-
- // -section-relocations, -sr
+ cl::alias FileHeadersShort("h", cl::desc("Alias for --file-headers"),
+ cl::aliasopt(FileHeaders), cl::NotHidden);
+ cl::alias FileHeadersSingular("file-header",
+ cl::desc("Alias for --file-headers"),
+ cl::aliasopt(FileHeaders));
+
+ // -section-headers, -sections, -S
+ // Also -s in llvm-readobj mode.
+ cl::opt<bool> SectionHeaders("section-headers",
+ cl::desc("Display all section headers."));
+ cl::alias SectionsShortUpper("S", cl::desc("Alias for --section-headers"),
+ cl::aliasopt(SectionHeaders), cl::NotHidden);
+ cl::alias SectionHeadersAlias("sections",
+ cl::desc("Alias for --section-headers"),
+ cl::aliasopt(SectionHeaders), cl::NotHidden);
+
+ // -section-relocations
+ // Also -sr in llvm-readobj mode.
cl::opt<bool> SectionRelocations("section-relocations",
cl::desc("Display relocations for each section shown."));
- cl::alias SectionRelocationsShort("sr",
- cl::desc("Alias for --section-relocations"),
- cl::aliasopt(SectionRelocations));
- // -section-symbols, -st
+ // -section-symbols
+ // Also -st in llvm-readobj mode.
cl::opt<bool> SectionSymbols("section-symbols",
cl::desc("Display symbols for each section shown."));
- cl::alias SectionSymbolsShort("st",
- cl::desc("Alias for --section-symbols"),
- cl::aliasopt(SectionSymbols));
- // -section-data, -sd
+ // -section-data
+ // Also -sd in llvm-readobj mode.
cl::opt<bool> SectionData("section-data",
cl::desc("Display section data for each section shown."));
- cl::alias SectionDataShort("sd",
- cl::desc("Alias for --section-data"),
- cl::aliasopt(SectionData));
- // -relocations, -r
+ // -relocations, -relocs, -r
cl::opt<bool> Relocations("relocations",
cl::desc("Display the relocation entries in the file"));
- cl::alias RelocationsShort("r",
- cl::desc("Alias for --relocations"),
- cl::aliasopt(Relocations));
+ cl::alias RelocationsShort("r", cl::desc("Alias for --relocations"),
+ cl::aliasopt(Relocations), cl::NotHidden);
+ cl::alias RelocationsGNU("relocs", cl::desc("Alias for --relocations"),
+ cl::aliasopt(Relocations));
// -notes, -n
cl::opt<bool> Notes("notes", cl::desc("Display the ELF notes in the file"));
@@ -109,19 +123,19 @@ namespace opts {
cl::opt<bool> DynRelocs("dyn-relocations",
cl::desc("Display the dynamic relocation entries in the file"));
- // -symbols, -t
+ // -symbols
+ // Also -s in llvm-readelf mode, or -t in llvm-readobj mode.
cl::opt<bool> Symbols("symbols",
cl::desc("Display the symbol table"));
- cl::alias SymbolsShort("t",
- cl::desc("Alias for --symbols"),
- cl::aliasopt(Symbols));
+ cl::alias SymbolsGNU("syms", cl::desc("Alias for --symbols"),
+ cl::aliasopt(Symbols));
- // -dyn-symbols, -dt
+ // -dyn-symbols, -dyn-syms
+ // Also -dt in llvm-readobj mode.
cl::opt<bool> DynamicSymbols("dyn-symbols",
cl::desc("Display the dynamic symbol table"));
- cl::alias DynamicSymbolsShort("dt",
- cl::desc("Alias for --dyn-symbols"),
- cl::aliasopt(DynamicSymbols));
+ cl::alias DynSymsGNU("dyn-syms", cl::desc("Alias for --dyn-symbols"),
+ cl::aliasopt(DynamicSymbols));
// -unwind, -u
cl::opt<bool> UnwindInfo("unwind",
@@ -130,29 +144,33 @@ namespace opts {
cl::desc("Alias for --unwind"),
cl::aliasopt(UnwindInfo));
- // -dynamic-table
+ // -dynamic-table, -dynamic, -d
cl::opt<bool> DynamicTable("dynamic-table",
cl::desc("Display the ELF .dynamic section table"));
cl::alias DynamicTableShort("d", cl::desc("Alias for --dynamic-table"),
+ cl::aliasopt(DynamicTable), cl::NotHidden);
+ cl::alias DynamicTableAlias("dynamic", cl::desc("Alias for --dynamic-table"),
cl::aliasopt(DynamicTable));
// -needed-libs
cl::opt<bool> NeededLibraries("needed-libs",
cl::desc("Display the needed libraries"));
- // -program-headers
+ // -program-headers, -segments, -l
cl::opt<bool> ProgramHeaders("program-headers",
cl::desc("Display ELF program headers"));
cl::alias ProgramHeadersShort("l", cl::desc("Alias for --program-headers"),
- cl::aliasopt(ProgramHeaders));
+ cl::aliasopt(ProgramHeaders), cl::NotHidden);
+ cl::alias SegmentsAlias("segments", cl::desc("Alias for --program-headers"),
+ cl::aliasopt(ProgramHeaders));
- // -string-dump
+ // -string-dump, -p
cl::list<std::string> StringDump("string-dump", cl::desc("<number|name>"),
cl::ZeroOrMore);
cl::alias StringDumpShort("p", cl::desc("Alias for --string-dump"),
cl::aliasopt(StringDump));
- // -hex-dump
+ // -hex-dump, -x
cl::list<std::string> HexDump("hex-dump", cl::desc("<number|name>"),
cl::ZeroOrMore);
cl::alias HexDumpShort("x", cl::desc("Alias for --hex-dump"),
@@ -188,11 +206,9 @@ namespace opts {
"codeview-subsection-bytes",
cl::desc("Dump raw contents of codeview debug sections and records"));
- // -arm-attributes, -a
+ // -arm-attributes
cl::opt<bool> ARMAttributes("arm-attributes",
cl::desc("Display the ARM attributes section"));
- cl::alias ARMAttributesShort("a", cl::desc("Alias for --arm-attributes"),
- cl::aliasopt(ARMAttributes));
// -mips-plt-got
cl::opt<bool>
@@ -283,28 +299,40 @@ namespace opts {
PrintStackMap("stackmap",
cl::desc("Display contents of stackmap section"));
- // -version-info
+ // -version-info, -V
cl::opt<bool>
VersionInfo("version-info",
cl::desc("Display ELF version sections (if present)"));
cl::alias VersionInfoShort("V", cl::desc("Alias for -version-info"),
cl::aliasopt(VersionInfo));
+ // -elf-section-groups, -section-groups, -g
cl::opt<bool> SectionGroups("elf-section-groups",
cl::desc("Display ELF section group contents"));
+ cl::alias SectionGroupsAlias("section-groups",
+ cl::desc("Alias for -elf-sections-groups"),
+ cl::aliasopt(SectionGroups));
cl::alias SectionGroupsShort("g", cl::desc("Alias for -elf-sections-groups"),
cl::aliasopt(SectionGroups));
+
+ // -elf-hash-histogram, -histogram, -I
cl::opt<bool> HashHistogram(
"elf-hash-histogram",
cl::desc("Display bucket list histogram for hash sections"));
cl::alias HashHistogramShort("I", cl::desc("Alias for -elf-hash-histogram"),
cl::aliasopt(HashHistogram));
+ cl::alias HistogramAlias("histogram",
+ cl::desc("Alias for --elf-hash-histogram"),
+ cl::aliasopt(HashHistogram));
+ // -elf-cg-profile
cl::opt<bool> CGProfile("elf-cg-profile", cl::desc("Display callgraph profile section"));
- cl::opt<bool> Addrsig("elf-addrsig",
+ // -addrsig
+ cl::opt<bool> Addrsig("addrsig",
cl::desc("Display address-significance table"));
+ // -elf-output-style
cl::opt<OutputStyleTy>
Output("elf-output-style", cl::desc("Specify ELF dump style"),
cl::values(clEnumVal(LLVM, "LLVM default style"),
@@ -418,8 +446,8 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer) {
if (opts::FileHeaders)
Dumper->printFileHeaders();
- if (opts::Sections)
- Dumper->printSections();
+ if (opts::SectionHeaders)
+ Dumper->printSectionHeaders();
if (opts::Relocations)
Dumper->printRelocations();
if (opts::DynRelocs)
@@ -492,6 +520,8 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer) {
Dumper->printCOFFResources();
if (opts::COFFLoadConfig)
Dumper->printCOFFLoadConfig();
+ if (opts::Addrsig)
+ Dumper->printAddrsig();
if (opts::CodeView)
Dumper->printCodeViewDebugInfo();
if (opts::CodeViewMergedTypes)
@@ -586,21 +616,87 @@ static void dumpInput(StringRef File) {
reportError(File, readobj_error::unrecognized_file_format);
}
+/// Registers aliases that should only be allowed by readobj.
+static void registerReadobjAliases() {
+ // -s has meant --sections for a very long time in llvm-readobj despite
+ // meaning --symbols in readelf.
+ static cl::alias SectionsShort("s", cl::desc("Alias for --section-headers"),
+ cl::aliasopt(opts::SectionHeaders),
+ cl::NotHidden);
+
+ // Only register -t in llvm-readobj, as readelf reserves it for
+ // --section-details (not implemented yet).
+ static cl::alias SymbolsShort("t", cl::desc("Alias for --symbols"),
+ cl::aliasopt(opts::Symbols), cl::NotHidden);
+
+ // The following two-letter aliases are only provided for readobj, as readelf
+ // allows single-letter args to be grouped together.
+ static cl::alias SectionRelocationsShort(
+ "sr", cl::desc("Alias for --section-relocations"),
+ cl::aliasopt(opts::SectionRelocations));
+ static cl::alias SectionDataShort("sd", cl::desc("Alias for --section-data"),
+ cl::aliasopt(opts::SectionData));
+ static cl::alias SectionSymbolsShort("st",
+ cl::desc("Alias for --section-symbols"),
+ cl::aliasopt(opts::SectionSymbols));
+ static cl::alias DynamicSymbolsShort("dt",
+ cl::desc("Alias for --dyn-symbols"),
+ cl::aliasopt(opts::DynamicSymbols));
+}
+
+/// Registers aliases that should only be allowed by readelf.
+static void registerReadelfAliases() {
+ // -s is here because for readobj it means --sections.
+ static cl::alias SymbolsShort("s", cl::desc("Alias for --symbols"),
+ cl::aliasopt(opts::Symbols), cl::NotHidden,
+ cl::Grouping);
+
+ // Allow all single letter flags to be grouped together.
+ for (auto &OptEntry : cl::getRegisteredOptions()) {
+ StringRef ArgName = OptEntry.getKey();
+ cl::Option *Option = OptEntry.getValue();
+ if (ArgName.size() == 1)
+ Option->setFormattingFlag(cl::Grouping);
+ }
+}
+
int main(int argc, const char *argv[]) {
InitLLVM X(argc, argv);
// Register the target printer for --version.
cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
- opts::WideOutput.setHiddenFlag(cl::Hidden);
-
- if (sys::path::stem(argv[0]).find("readelf") != StringRef::npos)
+ if (sys::path::stem(argv[0]).contains("readelf")) {
opts::Output = opts::GNU;
+ registerReadelfAliases();
+ } else {
+ registerReadobjAliases();
+ }
cl::ParseCommandLineOptions(argc, argv, "LLVM Object Reader\n");
+ if (opts::All) {
+ opts::FileHeaders = true;
+ opts::ProgramHeaders = true;
+ opts::SectionHeaders = true;
+ opts::Symbols = true;
+ opts::Relocations = true;
+ opts::DynamicTable = true;
+ opts::Notes = true;
+ opts::VersionInfo = true;
+ opts::UnwindInfo = true;
+ opts::SectionGroups = true;
+ opts::HashHistogram = true;
+ }
+
+ if (opts::Headers) {
+ opts::FileHeaders = true;
+ opts::ProgramHeaders = true;
+ opts::SectionHeaders = true;
+ }
+
// Default to stdin if no filename is specified.
- if (opts::InputFilenames.size() == 0)
+ if (opts::InputFilenames.empty())
opts::InputFilenames.push_back("-");
llvm::for_each(opts::InputFilenames, dumpInput);
diff --git a/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h
index 374ffd03e13a..92ed098dc642 100644
--- a/tools/llvm-readobj/llvm-readobj.h
+++ b/tools/llvm-readobj/llvm-readobj.h
@@ -40,7 +40,7 @@ namespace llvm {
return *EO;
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(EO.takeError(), OS, "");
+ logAllUnhandledErrors(EO.takeError(), OS);
OS.flush();
reportError(Buf);
}
@@ -49,22 +49,13 @@ namespace llvm {
} // namespace llvm
namespace opts {
- extern llvm::cl::list<std::string> InputFilenames;
- extern llvm::cl::opt<bool> FileHeaders;
- extern llvm::cl::opt<bool> Sections;
extern llvm::cl::opt<bool> SectionRelocations;
extern llvm::cl::opt<bool> SectionSymbols;
extern llvm::cl::opt<bool> SectionData;
- extern llvm::cl::opt<bool> Relocations;
- extern llvm::cl::opt<bool> Symbols;
extern llvm::cl::opt<bool> DynamicSymbols;
- extern llvm::cl::opt<bool> UnwindInfo;
extern llvm::cl::opt<bool> ExpandRelocs;
extern llvm::cl::opt<bool> RawRelr;
- extern llvm::cl::opt<bool> CodeView;
extern llvm::cl::opt<bool> CodeViewSubsectionBytes;
- extern llvm::cl::opt<bool> ARMAttributes;
- extern llvm::cl::opt<bool> MipsPLTGOT;
enum OutputStyleTy { LLVM, GNU };
extern llvm::cl::opt<OutputStyleTy> Output;
} // namespace opts
diff --git a/tools/llvm-rtdyld/llvm-rtdyld.cpp b/tools/llvm-rtdyld/llvm-rtdyld.cpp
index 54db1ec113fc..975638ed82d1 100644
--- a/tools/llvm-rtdyld/llvm-rtdyld.cpp
+++ b/tools/llvm-rtdyld/llvm-rtdyld.cpp
@@ -88,25 +88,30 @@ CheckFiles("check",
cl::desc("File containing RuntimeDyld verifier checks."),
cl::ZeroOrMore);
-static cl::opt<uint64_t>
+// Tracking BUG: 19665
+// http://llvm.org/bugs/show_bug.cgi?id=19665
+//
+// Do not change these options to cl::opt<uint64_t> since this silently breaks
+// argument parsing.
+static cl::opt<unsigned long long>
PreallocMemory("preallocate",
cl::desc("Allocate memory upfront rather than on-demand"),
cl::init(0));
-static cl::opt<uint64_t>
+static cl::opt<unsigned long long>
TargetAddrStart("target-addr-start",
cl::desc("For -verify only: start of phony target address "
"range."),
cl::init(4096), // Start at "page 1" - no allocating at "null".
cl::Hidden);
-static cl::opt<uint64_t>
+static cl::opt<unsigned long long>
TargetAddrEnd("target-addr-end",
cl::desc("For -verify only: end of phony target address range."),
cl::init(~0ULL),
cl::Hidden);
-static cl::opt<uint64_t>
+static cl::opt<unsigned long long>
TargetSectionSep("target-section-sep",
cl::desc("For -verify only: Separation between sections in "
"phony target address space."),
@@ -304,7 +309,7 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) {
if (!MaybeObj) {
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(MaybeObj.takeError(), OS, "");
+ logAllUnhandledErrors(MaybeObj.takeError(), OS);
OS.flush();
ErrorAndExit("unable to create object file: '" + Buf + "'");
}
@@ -433,7 +438,7 @@ static int executeInput() {
if (!MaybeObj) {
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(MaybeObj.takeError(), OS, "");
+ logAllUnhandledErrors(MaybeObj.takeError(), OS);
OS.flush();
ErrorAndExit("unable to create object file: '" + Buf + "'");
}
@@ -577,7 +582,11 @@ static void remapSectionsAndSymbols(const llvm::Triple &TargetTriple,
if (LoadAddr &&
*LoadAddr != static_cast<uint64_t>(
reinterpret_cast<uintptr_t>(Tmp->first))) {
- AlreadyAllocated[*LoadAddr] = Tmp->second;
+ // A section will have a LoadAddr of 0 if it wasn't loaded for whatever
+ // reason (e.g. zero byte COFF sections). Don't include those sections in
+ // the allocation map.
+ if (*LoadAddr != 0)
+ AlreadyAllocated[*LoadAddr] = Tmp->second;
Worklist.erase(Tmp);
}
}
@@ -701,7 +710,7 @@ static int linkAndVerify() {
if (!MaybeObj) {
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(MaybeObj.takeError(), OS, "");
+ logAllUnhandledErrors(MaybeObj.takeError(), OS);
OS.flush();
ErrorAndExit("unable to create object file: '" + Buf + "'");
}
diff --git a/tools/llvm-shlib/CMakeLists.txt b/tools/llvm-shlib/CMakeLists.txt
index 836024eb8101..187066e5ded2 100644
--- a/tools/llvm-shlib/CMakeLists.txt
+++ b/tools/llvm-shlib/CMakeLists.txt
@@ -6,81 +6,88 @@ set(SOURCES
libllvm.cpp
)
-llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS})
-
if(LLVM_LINK_LLVM_DYLIB AND LLVM_DYLIB_EXPORTED_SYMBOL_FILE)
message(WARNING "Using LLVM_LINK_LLVM_DYLIB with LLVM_DYLIB_EXPORTED_SYMBOL_FILE may not work. Use at your own risk.")
endif()
-# libLLVM.so should not have any dependencies on any other LLVM
-# shared libraries. When using the "all" pseudo-component,
-# LLVM_AVAILABLE_LIBS is added to the dependencies, which may
-# contain shared libraries (e.g. libLTO).
-#
-# Also exclude libLLVMTableGen for the following reasons:
-# - it is only used by internal *-tblgen utilities;
-# - it pollutes the global options space.
-foreach(lib ${LIB_NAMES})
- get_target_property(t ${lib} TYPE)
- if("${lib}" STREQUAL "LLVMTableGen")
- elseif("x${t}" STREQUAL "xSTATIC_LIBRARY")
- list(APPEND FILTERED_LIB_NAMES ${lib})
+if(LLVM_BUILD_LLVM_DYLIB)
+ if(MSVC)
+ message(FATAL_ERROR "Generating libLLVM is not supported on MSVC")
endif()
-endforeach()
-set(LIB_NAMES ${FILTERED_LIB_NAMES})
-
-if(LLVM_DYLIB_EXPORTED_SYMBOL_FILE)
- set(LLVM_EXPORTED_SYMBOL_FILE ${LLVM_DYLIB_EXPORTED_SYMBOL_FILE})
- add_custom_target(libLLVMExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE})
-endif()
-add_llvm_library(LLVM SHARED DISABLE_LLVM_LINK_LLVM_DYLIB SONAME ${SOURCES})
-
-list(REMOVE_DUPLICATES LIB_NAMES)
-if(("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") OR (MINGW) OR (HAIKU)
- OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD")
- OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD")
- OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia")
- OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "DragonFly")
- OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS")) # FIXME: It should be "GNU ld for elf"
- configure_file(
- ${CMAKE_CURRENT_SOURCE_DIR}/simple_version_script.map.in
- ${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map)
-
- # GNU ld doesn't resolve symbols in the version script.
- set(LIB_NAMES -Wl,--whole-archive ${LIB_NAMES} -Wl,--no-whole-archive)
- if (NOT LLVM_LINKER_IS_SOLARISLD)
- # Solaris ld does not accept global: *; so there is no way to version *all* global symbols
- set(LIB_NAMES -Wl,--version-script,${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map ${LIB_NAMES})
+ llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS})
+
+ # libLLVM.so should not have any dependencies on any other LLVM
+ # shared libraries. When using the "all" pseudo-component,
+ # LLVM_AVAILABLE_LIBS is added to the dependencies, which may
+ # contain shared libraries (e.g. libLTO).
+ #
+ # Also exclude libLLVMTableGen for the following reasons:
+ # - it is only used by internal *-tblgen utilities;
+ # - it pollutes the global options space.
+ foreach(lib ${LIB_NAMES})
+ get_target_property(t ${lib} TYPE)
+ if("${lib}" STREQUAL "LLVMTableGen")
+ elseif("x${t}" STREQUAL "xSTATIC_LIBRARY")
+ list(APPEND FILTERED_LIB_NAMES ${lib})
+ endif()
+ endforeach()
+ set(LIB_NAMES ${FILTERED_LIB_NAMES})
+
+ if(LLVM_DYLIB_EXPORTED_SYMBOL_FILE)
+ set(LLVM_EXPORTED_SYMBOL_FILE ${LLVM_DYLIB_EXPORTED_SYMBOL_FILE})
+ add_custom_target(libLLVMExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE})
endif()
-elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
- set(LIB_NAMES -Wl,-all_load ${LIB_NAMES})
-endif()
-target_link_libraries(LLVM PRIVATE ${LIB_NAMES})
+ add_llvm_library(LLVM SHARED DISABLE_LLVM_LINK_LLVM_DYLIB SONAME ${SOURCES})
+
+ list(REMOVE_DUPLICATES LIB_NAMES)
+ if(("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") OR (MINGW) OR (HAIKU)
+ OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD")
+ OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "GNU")
+ OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD")
+ OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia")
+ OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "DragonFly")
+ OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS")) # FIXME: It should be "GNU ld for elf"
+ configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/simple_version_script.map.in
+ ${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map)
+
+ # GNU ld doesn't resolve symbols in the version script.
+ set(LIB_NAMES -Wl,--whole-archive ${LIB_NAMES} -Wl,--no-whole-archive)
+ if (NOT LLVM_LINKER_IS_SOLARISLD)
+ # Solaris ld does not accept global: *; so there is no way to version *all* global symbols
+ set(LIB_NAMES -Wl,--version-script,${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map ${LIB_NAMES})
+ endif()
+ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
+ set(LIB_NAMES -Wl,-all_load ${LIB_NAMES})
+ endif()
-if (LLVM_DYLIB_SYMBOL_VERSIONING)
- set_property(TARGET LLVM APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--default-symver")
-endif()
+ target_link_libraries(LLVM PRIVATE ${LIB_NAMES})
-if (APPLE)
- set_property(TARGET LLVM APPEND_STRING PROPERTY
- LINK_FLAGS
- " -compatibility_version 1 -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}")
-endif()
+ if (APPLE)
+ set_property(TARGET LLVM APPEND_STRING PROPERTY
+ LINK_FLAGS
+ " -compatibility_version 1 -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}")
+ endif()
-if(TARGET libLLVMExports)
- add_dependencies(LLVM libLLVMExports)
+ if(TARGET libLLVMExports)
+ add_dependencies(LLVM libLLVMExports)
+ endif()
endif()
-if(LLVM_BUILD_LLVM_C_DYLIB)
- # To get the export list for a single llvm library:
- # nm ${LIB_PATH} | awk "/T _LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" > ${LIB_PATH}.exports
-
+if(LLVM_BUILD_LLVM_C_DYLIB AND NOT MSVC)
if(NOT APPLE)
message(FATAL_ERROR "Generating libLLVM-c is only supported on Darwin")
endif()
+ if(NOT LLVM_BUILD_LLVM_DYLIB)
+ message(FATAL_ERROR "Generating libLLVM-c requires LLVM_BUILD_LLVM_C_DYLIB on Darwin")
+ endif()
+
+ # To get the export list for a single llvm library:
+ # nm ${LIB_PATH} | awk "/T _LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" > ${LIB_PATH}.exports
+
set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_BINARY_DIR}/libllvm-c.exports)
set(LIB_DIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
@@ -108,3 +115,40 @@ if(LLVM_BUILD_LLVM_C_DYLIB)
" -compatibility_version 1 -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH} -Wl,-reexport_library ${LIB_PATH}")
endif()
+if(MSVC)
+ # Build the LLVM-C.dll library that exports the C API.
+
+ set(LLVM_LINK_COMPONENTS
+ ${LLVM_DYLIB_COMPONENTS}
+ )
+
+ llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS})
+ list(REMOVE_DUPLICATES LIB_NAMES)
+
+ # The python script needs to know whether symbols are prefixed with underscores or not.
+ if(LLVM_HOST_TRIPLE STREQUAL "i686-pc-win32")
+ set(GEN_UNDERSCORE "--underscore")
+ else()
+ set(GEN_UNDERSCORE "")
+ endif()
+
+ # Get the full name to the libs so the python script understands them.
+ foreach(lib ${LIB_NAMES})
+ list(APPEND FULL_LIB_NAMES ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib/${lib}.lib)
+ endforeach()
+
+ # Generate the exports file dynamically.
+ set(GEN_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/gen-msvc-exports.py)
+
+ set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/libllvm-c.exports)
+
+ add_custom_command(OUTPUT ${LLVM_EXPORTED_SYMBOL_FILE}
+ COMMAND ${PYTHON_EXECUTABLE} ${GEN_SCRIPT} ${FULL_LIB_NAMES} ${GEN_UNDERSCORE} --nm ${LLVM_TOOLS_BINARY_DIR}/llvm-nm -o ${LLVM_EXPORTED_SYMBOL_FILE}
+ DEPENDS ${LIB_NAMES} llvm-nm
+ COMMENT "Generating export list for LLVM-C"
+ VERBATIM )
+
+ # Finally link the target.
+ add_llvm_library(LLVM-C SHARED ${SOURCES} DEPENDS intrinsics_gen)
+
+endif()
diff --git a/tools/llvm-shlib/gen-msvc-exports.py b/tools/llvm-shlib/gen-msvc-exports.py
new file mode 100644
index 000000000000..6f0a6786d34f
--- /dev/null
+++ b/tools/llvm-shlib/gen-msvc-exports.py
@@ -0,0 +1,106 @@
+#===- gen-msvc-exports.py - Generate C API export file -------*- python -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+#
+# Generate an export file from a list of given LIB files. This only exports symbols
+# that start with LLVM, so it only exports the LLVM C API.
+#
+# To have CMake run this, set LLVM_BUILD_LLVM_C_DYLIB to on while
+# building on Windows.
+#
+# To run manually, build LLVM with Visual Studio, use a Command prompt
+# to navigate to the directory with the .lib files (Debug\lib etc). Then run
+# python C:\Path\To\gen-msvc-exports.py --nm ..\bin\llvm-nm.exe LLVM*.lib
+#
+# If you're generating a 32 bit DLL, use the `--underscore` flag.
+# If you want to use a different `llvm-nm` executable, pass the path
+# with the `--nm` flag.
+#
+# You can use the --output flag to set the name of the export file.
+#
+#===------------------------------------------------------------------------===#
+from tempfile import mkstemp
+from contextlib import contextmanager
+from subprocess import check_call
+import argparse
+import os
+import re
+
+
+_UNDERSCORE_REGEX = {
+ False: re.compile(r"^\w+\s+T\s+(LLVM.*)$"),
+ True: re.compile(r"^\w+\s+T\s+_(LLVM.*)$")
+}
+
+
+@contextmanager
+def removing(path):
+ try:
+ yield path
+ finally:
+ os.unlink(path)
+
+
+def touch_tempfile(*args, **kwargs):
+ fd, name = mkstemp(*args, **kwargs)
+ os.close(fd)
+ return name
+
+
+def gen_llvm_c_export(output, underscore, libs, nm):
+ """Generate the export file for the LLVM-C DLL.
+
+ Run `nm` for each lib in `libs`, and output an export file
+ to `output`. If `underscore` is true, symbols will
+ be assumed to be prefixed with an underscore.
+ """
+ with removing(touch_tempfile(prefix='dumpout', suffix='.txt')) as dumpout:
+
+ # Get the right regex.
+ p = _UNDERSCORE_REGEX[underscore]
+
+ with open(output, 'w+t') as output_f:
+
+ # For each lib get the LLVM* functions it exports.
+ for lib in libs:
+ # Call dumpbin.
+ with open(dumpout, 'w+t') as dumpout_f:
+ check_call([nm, '-g', lib], stdout=dumpout_f)
+
+ # Get the matching lines.
+ with open(dumpout) as dumpbin:
+ for line in dumpbin:
+ m = p.match(line)
+ if m is not None:
+ output_f.write(m.group(1) + '\n')
+
+
+def main():
+ parser = argparse.ArgumentParser('gen-msvc-exports')
+
+ parser.add_argument(
+ '-o', '--output', help='output filename', default='LLVM-C.exports'
+ )
+ parser.add_argument('-u', '--underscore',
+ help='labels are prefixed with an underscore (use for 32 bit DLLs)',
+ action='store_true'
+ )
+ parser.add_argument(
+ '--nm', help='path to the llvm-nm executable', default='llvm-nm'
+ )
+ parser.add_argument(
+ 'libs', metavar='LIBS', nargs='+', help='list of libraries to generate export from'
+ )
+
+ ns = parser.parse_args()
+
+ gen_llvm_c_export(ns.output, ns.underscore, ns.libs, ns.nm)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/llvm-size/llvm-size.cpp b/tools/llvm-size/llvm-size.cpp
index 67c81ec9e7cf..5d638443451f 100644
--- a/tools/llvm-size/llvm-size.cpp
+++ b/tools/llvm-size/llvm-size.cpp
@@ -71,9 +71,11 @@ ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"),
static bool ArchAll = false;
enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 };
-static cl::opt<unsigned int>
-Radix("radix", cl::desc("Print size in radix. Only 8, 10, and 16 are valid"),
- cl::init(decimal));
+static cl::opt<RadixTy> Radix(
+ "radix", cl::desc("Print size in radix"), cl::init(decimal),
+ cl::values(clEnumValN(octal, "8", "Print size in octal"),
+ clEnumValN(decimal, "10", "Print size in decimal"),
+ clEnumValN(hexadecimal, "16", "Print size in hexadecimal")));
static cl::opt<RadixTy>
RadixShort(cl::desc("Print size in radix:"),
@@ -138,7 +140,7 @@ static void error(llvm::Error E, StringRef FileName, const Archive::Child &C,
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(std::move(E), OS, "");
+ logAllUnhandledErrors(std::move(E), OS);
OS.flush();
errs() << " " << Buf << "\n";
}
@@ -156,7 +158,7 @@ static void error(llvm::Error E, StringRef FileName,
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(std::move(E), OS, "");
+ logAllUnhandledErrors(std::move(E), OS);
OS.flush();
errs() << " " << Buf << "\n";
}
@@ -455,8 +457,8 @@ static void printObjectSectionSizes(ObjectFile *Obj) {
// Make one pass over the section table to calculate sizes.
for (const SectionRef &Section : Obj->sections()) {
uint64_t size = Section.getSize();
- bool isText = Section.isText();
- bool isData = Section.isData();
+ bool isText = Section.isBerkeleyText();
+ bool isData = Section.isBerkeleyData();
bool isBSS = Section.isBSS();
if (isText)
total_text += size;
@@ -479,19 +481,25 @@ static void printObjectSectionSizes(ObjectFile *Obj) {
}
if (!BerkeleyHeaderPrinted) {
- outs() << " text data bss "
- << (Radix == octal ? "oct" : "dec") << " hex filename\n";
+ outs() << " text\t"
+ " data\t"
+ " bss\t"
+ " "
+ << (Radix == octal ? "oct" : "dec")
+ << "\t"
+ " hex\t"
+ "filename\n";
BerkeleyHeaderPrinted = true;
}
// Print result.
- fmt << "%#7" << radix_fmt << " "
- << "%#7" << radix_fmt << " "
- << "%#7" << radix_fmt << " ";
+ fmt << "%#7" << radix_fmt << "\t"
+ << "%#7" << radix_fmt << "\t"
+ << "%#7" << radix_fmt << "\t";
outs() << format(fmt.str().c_str(), total_text, total_data, total_bss);
fmtbuf.clear();
- fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << " "
- << "%7" PRIx64 " ";
+ fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t"
+ << "%7" PRIx64 "\t";
outs() << format(fmt.str().c_str(), total, total);
}
}
@@ -570,7 +578,7 @@ static void printFileSectionSizes(StringRef file) {
} else if (MachOUniversalBinary *UB =
dyn_cast<MachOUniversalBinary>(&Bin)) {
// If we have a list of architecture flags specified dump only those.
- if (!ArchAll && ArchFlags.size() != 0) {
+ if (!ArchAll && !ArchFlags.empty()) {
// Look for a slice in the universal binary that matches each ArchFlag.
bool ArchFound;
for (unsigned i = 0; i < ArchFlags.size(); ++i) {
@@ -839,14 +847,14 @@ static void printBerkelyTotals() {
std::string fmtbuf;
raw_string_ostream fmt(fmtbuf);
const char *radix_fmt = getRadixFmt();
- fmt << "%#7" << radix_fmt << " "
- << "%#7" << radix_fmt << " "
- << "%#7" << radix_fmt << " ";
+ fmt << "%#7" << radix_fmt << "\t"
+ << "%#7" << radix_fmt << "\t"
+ << "%#7" << radix_fmt << "\t";
outs() << format(fmt.str().c_str(), TotalObjectText, TotalObjectData,
TotalObjectBss);
fmtbuf.clear();
- fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << " "
- << "%7" PRIx64 " ";
+ fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t"
+ << "%7" PRIx64 "\t";
outs() << format(fmt.str().c_str(), TotalObjectTotal, TotalObjectTotal)
<< "(TOTALS)\n";
}
@@ -859,21 +867,21 @@ int main(int argc, char **argv) {
if (OutputFormatShort.getNumOccurrences())
OutputFormat = static_cast<OutputFormatTy>(OutputFormatShort);
if (RadixShort.getNumOccurrences())
- Radix = RadixShort;
+ Radix = RadixShort.getValue();
- for (unsigned i = 0; i < ArchFlags.size(); ++i) {
- if (ArchFlags[i] == "all") {
+ for (StringRef Arch : ArchFlags) {
+ if (Arch == "all") {
ArchAll = true;
} else {
- if (!MachOObjectFile::isValidArch(ArchFlags[i])) {
+ if (!MachOObjectFile::isValidArch(Arch)) {
outs() << ToolName << ": for the -arch option: Unknown architecture "
- << "named '" << ArchFlags[i] << "'";
+ << "named '" << Arch << "'";
return 1;
}
}
}
- if (InputFilenames.size() == 0)
+ if (InputFilenames.empty())
InputFilenames.push_back("a.out");
MoreThanOneFile = InputFilenames.size() > 1;
diff --git a/tools/llvm-stress/llvm-stress.cpp b/tools/llvm-stress/llvm-stress.cpp
index d8ec11251ff6..c29b7a7f7e46 100644
--- a/tools/llvm-stress/llvm-stress.cpp
+++ b/tools/llvm-stress/llvm-stress.cpp
@@ -356,8 +356,8 @@ struct StoreModifier: public Modifier {
void Act() override {
// Try to use predefined pointers. If non-exist, use undef pointer value;
Value *Ptr = getRandomPointerValue();
- Type *Tp = Ptr->getType();
- Value *Val = getRandomValue(Tp->getContainedType(0));
+ PointerType *Tp = cast<PointerType>(Ptr->getType());
+ Value *Val = getRandomValue(Tp->getElementType());
Type *ValTy = Val->getType();
// Do not store vectors of i1s because they are unsupported
diff --git a/tools/llvm-strings/llvm-strings.cpp b/tools/llvm-strings/llvm-strings.cpp
index 8e2d213bcc73..cdc2a6ef033b 100644
--- a/tools/llvm-strings/llvm-strings.cpp
+++ b/tools/llvm-strings/llvm-strings.cpp
@@ -60,27 +60,27 @@ static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) {
if (L.size() < static_cast<size_t>(MinLength))
return;
if (PrintFileName)
- OS << FileName << ":";
+ OS << FileName << ": ";
switch (Radix) {
case none:
break;
case octal:
- OS << format("%8o", Offset);
+ OS << format("%7o ", Offset);
break;
case hexadecimal:
- OS << format("%8x", Offset);
+ OS << format("%7x ", Offset);
break;
case decimal:
- OS << format("%8u", Offset);
+ OS << format("%7u ", Offset);
break;
}
- OS << " " << L << '\n';
+ OS << L << '\n';
};
const char *B = Contents.begin();
const char *P = nullptr, *E = nullptr, *S = nullptr;
for (P = Contents.begin(), E = Contents.end(); P < E; ++P) {
- if (std::isgraph(*P) || std::isblank(*P)) {
+ if (isPrint(*P) || *P == '\t') {
if (S == nullptr)
S = P;
} else if (S) {
diff --git a/tools/llvm-symbolizer/llvm-symbolizer.cpp b/tools/llvm-symbolizer/llvm-symbolizer.cpp
index 6d40a5403504..9d19f994b739 100644
--- a/tools/llvm-symbolizer/llvm-symbolizer.cpp
+++ b/tools/llvm-symbolizer/llvm-symbolizer.cpp
@@ -55,17 +55,29 @@ static cl::opt<bool>
ClPrintInlining("inlining", cl::init(true),
cl::desc("Print all inlined frames for a given address"));
+// -demangle, -C
static cl::opt<bool>
ClDemangle("demangle", cl::init(true), cl::desc("Demangle function names"));
+static cl::alias
+ClDemangleShort("C", cl::desc("Alias for -demangle"),
+ cl::NotHidden, cl::aliasopt(ClDemangle));
static cl::opt<std::string> ClDefaultArch("default-arch", cl::init(""),
cl::desc("Default architecture "
"(for multi-arch objects)"));
+// -obj, -exe, -e
static cl::opt<std::string>
ClBinaryName("obj", cl::init(""),
cl::desc("Path to object file to be symbolized (if not provided, "
"object file should be specified for each input line)"));
+static cl::alias
+ClBinaryNameAliasExe("exe", cl::desc("Alias for -obj"),
+ cl::NotHidden, cl::aliasopt(ClBinaryName));
+static cl::alias
+ClBinaryNameAliasE("e", cl::desc("Alias for -obj"),
+ cl::NotHidden, cl::aliasopt(ClBinaryName));
+
static cl::opt<std::string>
ClDwpName("dwp", cl::init(""),
@@ -75,13 +87,25 @@ static cl::list<std::string>
ClDsymHint("dsym-hint", cl::ZeroOrMore,
cl::desc("Path to .dSYM bundles to search for debug info for the "
"object files"));
-static cl::opt<bool>
- ClPrintAddress("print-address", cl::init(false),
- cl::desc("Show address before line information"));
+// -print-address, -addresses, -a
+static cl::opt<bool>
+ClPrintAddress("print-address", cl::init(false),
+ cl::desc("Show address before line information"));
+static cl::alias
+ClPrintAddressAliasAddresses("addresses", cl::desc("Alias for -print-address"),
+ cl::NotHidden, cl::aliasopt(ClPrintAddress));
+static cl::alias
+ClPrintAddressAliasA("a", cl::desc("Alias for -print-address"),
+ cl::NotHidden, cl::aliasopt(ClPrintAddress));
+
+// -pretty-print, -p
static cl::opt<bool>
ClPrettyPrint("pretty-print", cl::init(false),
cl::desc("Make the output more human friendly"));
+static cl::alias ClPrettyPrintShort("p", cl::desc("Alias for -pretty-print"),
+ cl::NotHidden,
+ cl::aliasopt(ClPrettyPrint));
static cl::opt<int> ClPrintSourceContextLines(
"print-source-context-lines", cl::init(0),
@@ -90,6 +114,10 @@ static cl::opt<int> ClPrintSourceContextLines(
static cl::opt<bool> ClVerbose("verbose", cl::init(false),
cl::desc("Print verbose line info"));
+static cl::list<std::string> ClInputAddresses(cl::Positional,
+ cl::desc("<input addresses>..."),
+ cl::ZeroOrMore);
+
template<typename T>
static bool error(Expected<T> &ResOrErr) {
if (ResOrErr)
@@ -137,6 +165,38 @@ static bool parseCommand(StringRef InputString, bool &IsData,
return !StringRef(pos, offset_length).getAsInteger(0, ModuleOffset);
}
+static void symbolizeInput(StringRef InputString, LLVMSymbolizer &Symbolizer,
+ DIPrinter &Printer) {
+ bool IsData = false;
+ std::string ModuleName;
+ uint64_t ModuleOffset = 0;
+ if (!parseCommand(StringRef(InputString), IsData, ModuleName, ModuleOffset)) {
+ outs() << InputString;
+ return;
+ }
+
+ if (ClPrintAddress) {
+ outs() << "0x";
+ outs().write_hex(ModuleOffset);
+ StringRef Delimiter = ClPrettyPrint ? ": " : "\n";
+ outs() << Delimiter;
+ }
+ if (IsData) {
+ auto ResOrErr = Symbolizer.symbolizeData(ModuleName, ModuleOffset);
+ Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get());
+ } else if (ClPrintInlining) {
+ auto ResOrErr =
+ Symbolizer.symbolizeInlinedCode(ModuleName, ModuleOffset, ClDwpName);
+ Printer << (error(ResOrErr) ? DIInliningInfo() : ResOrErr.get());
+ } else {
+ auto ResOrErr =
+ Symbolizer.symbolizeCode(ModuleName, ModuleOffset, ClDwpName);
+ Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get());
+ }
+ outs() << "\n";
+ outs().flush();
+}
+
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
@@ -159,43 +219,15 @@ int main(int argc, char **argv) {
DIPrinter Printer(outs(), ClPrintFunctions != FunctionNameKind::None,
ClPrettyPrint, ClPrintSourceContextLines, ClVerbose);
- const int kMaxInputStringLength = 1024;
- char InputString[kMaxInputStringLength];
-
- while (true) {
- if (!fgets(InputString, sizeof(InputString), stdin))
- break;
-
- bool IsData = false;
- std::string ModuleName;
- uint64_t ModuleOffset = 0;
- if (!parseCommand(StringRef(InputString), IsData, ModuleName,
- ModuleOffset)) {
- outs() << InputString;
- continue;
- }
+ if (ClInputAddresses.empty()) {
+ const int kMaxInputStringLength = 1024;
+ char InputString[kMaxInputStringLength];
- if (ClPrintAddress) {
- outs() << "0x";
- outs().write_hex(ModuleOffset);
- StringRef Delimiter = ClPrettyPrint ? ": " : "\n";
- outs() << Delimiter;
- }
- if (IsData) {
- auto ResOrErr = Symbolizer.symbolizeData(ModuleName, ModuleOffset);
- Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get());
- } else if (ClPrintInlining) {
- auto ResOrErr =
- Symbolizer.symbolizeInlinedCode(ModuleName, ModuleOffset, ClDwpName);
- Printer << (error(ResOrErr) ? DIInliningInfo()
- : ResOrErr.get());
- } else {
- auto ResOrErr =
- Symbolizer.symbolizeCode(ModuleName, ModuleOffset, ClDwpName);
- Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get());
- }
- outs() << "\n";
- outs().flush();
+ while (fgets(InputString, sizeof(InputString), stdin))
+ symbolizeInput(InputString, Symbolizer, Printer);
+ } else {
+ for (StringRef Address : ClInputAddresses)
+ symbolizeInput(Address, Symbolizer, Printer);
}
return 0;
diff --git a/tools/llvm-undname/llvm-undname.cpp b/tools/llvm-undname/llvm-undname.cpp
index 2124ec169455..60520c8f7be0 100644
--- a/tools/llvm-undname/llvm-undname.cpp
+++ b/tools/llvm-undname/llvm-undname.cpp
@@ -18,6 +18,7 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Process.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdio>
#include <cstring>
@@ -26,17 +27,25 @@
using namespace llvm;
+cl::opt<bool> DumpBackReferences("backrefs", cl::Optional,
+ cl::desc("dump backreferences"), cl::Hidden,
+ cl::init(false));
cl::list<std::string> Symbols(cl::Positional, cl::desc("<input symbols>"),
cl::ZeroOrMore);
static void demangle(const std::string &S) {
int Status;
- char *ResultBuf = microsoftDemangle(S.c_str(), nullptr, nullptr, &Status);
+ MSDemangleFlags Flags = MSDF_None;
+ if (DumpBackReferences)
+ Flags = MSDemangleFlags(Flags | MSDF_DumpBackrefs);
+
+ char *ResultBuf =
+ microsoftDemangle(S.c_str(), nullptr, nullptr, &Status, Flags);
if (Status == llvm::demangle_success) {
outs() << ResultBuf << "\n";
outs().flush();
} else {
- errs() << "Error: Invalid mangled name\n";
+ WithColor::error() << "Invalid mangled name\n";
}
std::free(ResultBuf);
}
diff --git a/tools/llvm-xray/CMakeLists.txt b/tools/llvm-xray/CMakeLists.txt
index 66b7f21c43fb..4b056d10758f 100644
--- a/tools/llvm-xray/CMakeLists.txt
+++ b/tools/llvm-xray/CMakeLists.txt
@@ -14,6 +14,7 @@ add_llvm_tool(llvm-xray
xray-color-helper.cpp
xray-converter.cpp
xray-extract.cpp
+ xray-fdr-dump.cpp
xray-graph-diff.cpp
xray-graph.cpp
xray-registry.cpp
diff --git a/tools/llvm-xray/xray-account.cpp b/tools/llvm-xray/xray-account.cpp
index 2776a8888481..9985c9adcf6c 100644
--- a/tools/llvm-xray/xray-account.cpp
+++ b/tools/llvm-xray/xray-account.cpp
@@ -146,6 +146,10 @@ bool LatencyAccountant::accountRecord(const XRayRecord &Record) {
auto &ThreadStack = PerThreadFunctionStack[Record.TId];
switch (Record.Type) {
+ case RecordTypes::CUSTOM_EVENT:
+ case RecordTypes::TYPED_EVENT:
+ // TODO: Support custom and typed event accounting in the future.
+ return true;
case RecordTypes::ENTER:
case RecordTypes::ENTER_ARG: {
ThreadStack.emplace_back(Record.FuncId, Record.TSC);
@@ -255,9 +259,18 @@ ResultRow getStats(std::vector<uint64_t> &Timings) {
} // namespace
+using TupleType = std::tuple<int32_t, uint64_t, ResultRow>;
+
+template <typename F>
+static void sortByKey(std::vector<TupleType> &Results, F Fn) {
+ bool ASC = AccountSortOrder == SortDirection::ASCENDING;
+ llvm::sort(Results, [=](const TupleType &L, const TupleType &R) {
+ return ASC ? Fn(L) < Fn(R) : Fn(L) > Fn(R);
+ });
+}
+
template <class F>
void LatencyAccountant::exportStats(const XRayFileHeader &Header, F Fn) const {
- using TupleType = std::tuple<int32_t, uint64_t, ResultRow>;
std::vector<TupleType> Results;
Results.reserve(FunctionLatencies.size());
for (auto FT : FunctionLatencies) {
@@ -282,84 +295,38 @@ void LatencyAccountant::exportStats(const XRayFileHeader &Header, F Fn) const {
// Sort the data according to user-provided flags.
switch (AccountSortOutput) {
case SortField::FUNCID:
- llvm::sort(Results.begin(), Results.end(),
- [](const TupleType &L, const TupleType &R) {
- if (AccountSortOrder == SortDirection::ASCENDING)
- return std::get<0>(L) < std::get<0>(R);
- if (AccountSortOrder == SortDirection::DESCENDING)
- return std::get<0>(L) > std::get<0>(R);
- llvm_unreachable("Unknown sort direction");
- });
+ sortByKey(Results, [](const TupleType &X) { return std::get<0>(X); });
break;
case SortField::COUNT:
- llvm::sort(Results.begin(), Results.end(),
- [](const TupleType &L, const TupleType &R) {
- if (AccountSortOrder == SortDirection::ASCENDING)
- return std::get<1>(L) < std::get<1>(R);
- if (AccountSortOrder == SortDirection::DESCENDING)
- return std::get<1>(L) > std::get<1>(R);
- llvm_unreachable("Unknown sort direction");
- });
+ sortByKey(Results, [](const TupleType &X) { return std::get<1>(X); });
+ break;
+ case SortField::MIN:
+ sortByKey(Results, [](const TupleType &X) { return std::get<2>(X).Min; });
+ break;
+ case SortField::MED:
+ sortByKey(Results, [](const TupleType &X) { return std::get<2>(X).Median; });
+ break;
+ case SortField::PCT90:
+ sortByKey(Results, [](const TupleType &X) { return std::get<2>(X).Pct90; });
+ break;
+ case SortField::PCT99:
+ sortByKey(Results, [](const TupleType &X) { return std::get<2>(X).Pct99; });
break;
- default:
- // Here we need to look into the ResultRow for the rest of the data that
- // we want to sort by.
- llvm::sort(Results.begin(), Results.end(),
- [&](const TupleType &L, const TupleType &R) {
- auto &LR = std::get<2>(L);
- auto &RR = std::get<2>(R);
- switch (AccountSortOutput) {
- case SortField::COUNT:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Count < RR.Count;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Count > RR.Count;
- llvm_unreachable("Unknown sort direction");
- case SortField::MIN:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Min < RR.Min;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Min > RR.Min;
- llvm_unreachable("Unknown sort direction");
- case SortField::MED:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Median < RR.Median;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Median > RR.Median;
- llvm_unreachable("Unknown sort direction");
- case SortField::PCT90:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Pct90 < RR.Pct90;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Pct90 > RR.Pct90;
- llvm_unreachable("Unknown sort direction");
- case SortField::PCT99:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Pct99 < RR.Pct99;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Pct99 > RR.Pct99;
- llvm_unreachable("Unknown sort direction");
- case SortField::MAX:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Max < RR.Max;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Max > RR.Max;
- llvm_unreachable("Unknown sort direction");
- case SortField::SUM:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Sum < RR.Sum;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Sum > RR.Sum;
- llvm_unreachable("Unknown sort direction");
- default:
- llvm_unreachable("Unsupported sort order");
- }
- });
+ case SortField::MAX:
+ sortByKey(Results, [](const TupleType &X) { return std::get<2>(X).Max; });
break;
+ case SortField::SUM:
+ sortByKey(Results, [](const TupleType &X) { return std::get<2>(X).Sum; });
+ break;
+ case SortField::FUNC:
+ llvm_unreachable("Not implemented");
}
- if (AccountTop > 0)
- Results.erase(Results.begin() + AccountTop.getValue(), Results.end());
+ if (AccountTop > 0) {
+ auto MaxTop =
+ std::min(AccountTop.getValue(), static_cast<int>(Results.size()));
+ Results.erase(Results.begin() + MaxTop, Results.end());
+ }
for (const auto &R : Results)
Fn(std::get<0>(R), std::get<1>(R), std::get<2>(R));
@@ -417,19 +384,25 @@ namespace llvm {
template <> struct format_provider<llvm::xray::RecordTypes> {
static void format(const llvm::xray::RecordTypes &T, raw_ostream &Stream,
StringRef Style) {
- switch(T) {
- case RecordTypes::ENTER:
- Stream << "enter";
- break;
- case RecordTypes::ENTER_ARG:
- Stream << "enter-arg";
- break;
- case RecordTypes::EXIT:
- Stream << "exit";
- break;
- case RecordTypes::TAIL_EXIT:
- Stream << "tail-exit";
- break;
+ switch (T) {
+ case RecordTypes::ENTER:
+ Stream << "enter";
+ break;
+ case RecordTypes::ENTER_ARG:
+ Stream << "enter-arg";
+ break;
+ case RecordTypes::EXIT:
+ Stream << "exit";
+ break;
+ case RecordTypes::TAIL_EXIT:
+ Stream << "tail-exit";
+ break;
+ case RecordTypes::CUSTOM_EVENT:
+ Stream << "custom-event";
+ break;
+ case RecordTypes::TYPED_EVENT:
+ Stream << "typed-event";
+ break;
}
}
};
diff --git a/tools/llvm-xray/xray-converter.cpp b/tools/llvm-xray/xray-converter.cpp
index 90e14d0d8896..3f153b99bc93 100644
--- a/tools/llvm-xray/xray-converter.cpp
+++ b/tools/llvm-xray/xray-converter.cpp
@@ -18,6 +18,7 @@
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/JSON.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
@@ -91,9 +92,10 @@ void TraceConverter::exportAsYAML(const Trace &Records, raw_ostream &OS) {
Trace.Records.push_back({R.RecordType, R.CPU, R.Type, R.FuncId,
Symbolize ? FuncIdHelper.SymbolOrNumber(R.FuncId)
: llvm::to_string(R.FuncId),
- R.TSC, R.TId, R.PId, R.CallArgs});
+ R.TSC, R.TId, R.PId, R.CallArgs, R.Data});
}
Output Out(OS, nullptr, 0);
+ Out.setWriteDefaultValues(false);
Out << Trace;
}
@@ -122,21 +124,27 @@ void TraceConverter::exportAsRAWv1(const Trace &Records, raw_ostream &OS) {
// Then write out the rest of the records, still in an endian-appropriate
// format.
for (const auto &R : Records) {
- Writer.write(R.RecordType);
- // The on disk naive raw format uses 8 bit CPUs, but the record has 16.
- // There's no choice but truncation.
- Writer.write(static_cast<uint8_t>(R.CPU));
switch (R.Type) {
case RecordTypes::ENTER:
case RecordTypes::ENTER_ARG:
+ Writer.write(R.RecordType);
+ Writer.write(static_cast<uint8_t>(R.CPU));
Writer.write(uint8_t{0});
break;
case RecordTypes::EXIT:
+ Writer.write(R.RecordType);
+ Writer.write(static_cast<uint8_t>(R.CPU));
Writer.write(uint8_t{1});
break;
case RecordTypes::TAIL_EXIT:
+ Writer.write(R.RecordType);
+ Writer.write(static_cast<uint8_t>(R.CPU));
Writer.write(uint8_t{2});
break;
+ case RecordTypes::CUSTOM_EVENT:
+ case RecordTypes::TYPED_EVENT:
+ // Skip custom and typed event records for v1 logs.
+ continue;
}
Writer.write(R.FuncId);
Writer.write(R.TSC);
@@ -234,31 +242,6 @@ StackTrieNode *findOrCreateStackNode(
return CurrentStack;
}
-void writeTraceViewerRecord(uint16_t Version, raw_ostream &OS, int32_t FuncId,
- uint32_t TId, uint32_t PId, bool Symbolize,
- const FuncIdConversionHelper &FuncIdHelper,
- double EventTimestampUs,
- const StackTrieNode &StackCursor,
- StringRef FunctionPhenotype) {
- OS << " ";
- if (Version >= 3) {
- OS << llvm::formatv(
- R"({ "name" : "{0}", "ph" : "{1}", "tid" : "{2}", "pid" : "{3}", )"
- R"("ts" : "{4:f4}", "sf" : "{5}" })",
- (Symbolize ? FuncIdHelper.SymbolOrNumber(FuncId)
- : llvm::to_string(FuncId)),
- FunctionPhenotype, TId, PId, EventTimestampUs,
- StackCursor.ExtraData.id);
- } else {
- OS << llvm::formatv(
- R"({ "name" : "{0}", "ph" : "{1}", "tid" : "{2}", "pid" : "1", )"
- R"("ts" : "{3:f3}", "sf" : "{4}" })",
- (Symbolize ? FuncIdHelper.SymbolOrNumber(FuncId)
- : llvm::to_string(FuncId)),
- FunctionPhenotype, TId, EventTimestampUs, StackCursor.ExtraData.id);
- }
-}
-
} // namespace
void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records,
@@ -269,18 +252,14 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records,
unsigned id_counter = 0;
- OS << "{\n \"traceEvents\": [";
DenseMap<uint32_t, StackTrieNode *> StackCursorByThreadId{};
DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>> StackRootsByThreadId{};
DenseMap<unsigned, StackTrieNode *> StacksByStackId{};
std::forward_list<StackTrieNode> NodeStore{};
- int loop_count = 0;
- for (const auto &R : Records) {
- if (loop_count++ == 0)
- OS << "\n";
- else
- OS << ",\n";
+ // Create a JSON Array which will hold all trace events.
+ json::Array TraceEvents;
+ for (const auto &R : Records) {
// Chrome trace event format always wants data in micros.
// CyclesPerMicro = CycleHertz / 10^6
// TSC / CyclesPerMicro == TSC * 10^6 / CycleHertz == MicroTimestamp
@@ -292,6 +271,10 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records,
double EventTimestampUs = double(1000000) / CycleFreq * double(R.TSC);
StackTrieNode *&StackCursor = StackCursorByThreadId[R.TId];
switch (R.Type) {
+ case RecordTypes::CUSTOM_EVENT:
+ case RecordTypes::TYPED_EVENT:
+ // TODO: Support typed and custom event rendering on Chrome Trace Viewer.
+ break;
case RecordTypes::ENTER:
case RecordTypes::ENTER_ARG:
StackCursor = findOrCreateStackNode(StackCursor, R.FuncId, R.TId,
@@ -301,8 +284,15 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records,
// type of B for begin or E for end, thread id, process id,
// timestamp in microseconds, and a stack frame id. The ids are logged
// in an id dictionary after the events.
- writeTraceViewerRecord(Version, OS, R.FuncId, R.TId, R.PId, Symbolize,
- FuncIdHelper, EventTimestampUs, *StackCursor, "B");
+ TraceEvents.push_back(json::Object({
+ {"name", Symbolize ? FuncIdHelper.SymbolOrNumber(R.FuncId)
+ : llvm::to_string(R.FuncId)},
+ {"ph", "B"},
+ {"tid", llvm::to_string(R.TId)},
+ {"pid", llvm::to_string(Version >= 3 ? R.PId : 1)},
+ {"ts", llvm::formatv("{0:f4}", EventTimestampUs)},
+ {"sf", llvm::to_string(StackCursor->ExtraData.id)},
+ }));
break;
case RecordTypes::EXIT:
case RecordTypes::TAIL_EXIT:
@@ -313,43 +303,51 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records,
// (And/Or in loop termination below)
StackTrieNode *PreviousCursor = nullptr;
do {
- if (PreviousCursor != nullptr) {
- OS << ",\n";
- }
- writeTraceViewerRecord(Version, OS, StackCursor->FuncId, R.TId, R.PId,
- Symbolize, FuncIdHelper, EventTimestampUs,
- *StackCursor, "E");
+ TraceEvents.push_back(json::Object({
+ {"name", Symbolize
+ ? FuncIdHelper.SymbolOrNumber(StackCursor->FuncId)
+ : llvm::to_string(StackCursor->FuncId)},
+ {"ph", "E"},
+ {"tid", llvm::to_string(R.TId)},
+ {"pid", llvm::to_string(Version >= 3 ? R.PId : 1)},
+ {"ts", llvm::formatv("{0:f4}", EventTimestampUs)},
+ {"sf", llvm::to_string(StackCursor->ExtraData.id)},
+ }));
PreviousCursor = StackCursor;
StackCursor = StackCursor->Parent;
} while (PreviousCursor->FuncId != R.FuncId && StackCursor != nullptr);
break;
}
}
- OS << "\n ],\n"; // Close the Trace Events array.
- OS << " "
- << "\"displayTimeUnit\": \"ns\",\n";
// The stackFrames dictionary substantially reduces size of the output file by
// avoiding repeating the entire call stack of function names for each entry.
- OS << R"( "stackFrames": {)";
- int stack_frame_count = 0;
- for (auto map_iter : StacksByStackId) {
- if (stack_frame_count++ == 0)
- OS << "\n";
- else
- OS << ",\n";
- OS << " ";
- OS << llvm::formatv(
- R"("{0}" : { "name" : "{1}")", map_iter.first,
- (Symbolize ? FuncIdHelper.SymbolOrNumber(map_iter.second->FuncId)
- : llvm::to_string(map_iter.second->FuncId)));
- if (map_iter.second->Parent != nullptr)
- OS << llvm::formatv(R"(, "parent": "{0}")",
- map_iter.second->Parent->ExtraData.id);
- OS << " }";
+ json::Object StackFrames;
+ for (const auto &Stack : StacksByStackId) {
+ const auto &StackId = Stack.first;
+ const auto &StackFunctionNode = Stack.second;
+ json::Object::iterator It;
+ std::tie(It, std::ignore) = StackFrames.insert({
+ llvm::to_string(StackId),
+ json::Object{
+ {"name",
+ Symbolize ? FuncIdHelper.SymbolOrNumber(StackFunctionNode->FuncId)
+ : llvm::to_string(StackFunctionNode->FuncId)}},
+ });
+
+ if (StackFunctionNode->Parent != nullptr)
+ It->second.getAsObject()->insert(
+ {"parent", llvm::to_string(StackFunctionNode->Parent->ExtraData.id)});
}
- OS << "\n }\n"; // Close the stack frames map.
- OS << "}\n"; // Close the JSON entry.
+
+ json::Object TraceJSON{
+ {"displayTimeUnit", "ns"},
+ {"traceEvents", std::move(TraceEvents)},
+ {"stackFrames", std::move(StackFrames)},
+ };
+
+ // Pretty-print the JSON using two spaces for indentations.
+ OS << formatv("{0:2}", json::Value(std::move(TraceJSON)));
}
namespace llvm {
diff --git a/tools/llvm-xray/xray-fdr-dump.cpp b/tools/llvm-xray/xray-fdr-dump.cpp
new file mode 100644
index 000000000000..389825605b62
--- /dev/null
+++ b/tools/llvm-xray/xray-fdr-dump.cpp
@@ -0,0 +1,119 @@
+//===- xray-fdr-dump.cpp: XRay FDR Trace Dump Tool ------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements the FDR trace dumping tool, using the libraries for handling FDR
+// mode traces specifically.
+//
+//===----------------------------------------------------------------------===//
+#include "xray-registry.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/XRay/BlockIndexer.h"
+#include "llvm/XRay/BlockPrinter.h"
+#include "llvm/XRay/BlockVerifier.h"
+#include "llvm/XRay/FDRRecordConsumer.h"
+#include "llvm/XRay/FDRRecordProducer.h"
+#include "llvm/XRay/FDRRecords.h"
+#include "llvm/XRay/FileHeaderReader.h"
+#include "llvm/XRay/RecordPrinter.h"
+
+using namespace llvm;
+using namespace xray;
+
+static cl::SubCommand Dump("fdr-dump", "FDR Trace Dump");
+static cl::opt<std::string> DumpInput(cl::Positional,
+ cl::desc("<xray fdr mode log>"),
+ cl::Required, cl::sub(Dump));
+static cl::opt<bool> DumpVerify("verify",
+ cl::desc("verify structure of the log"),
+ cl::init(false), cl::sub(Dump));
+
+static CommandRegistration Unused(&Dump, []() -> Error {
+ // Open the file provided.
+ int Fd;
+ if (auto EC = sys::fs::openFileForRead(DumpInput, Fd))
+ return createStringError(EC, "Cannot open file '%s' for read.",
+ DumpInput.c_str());
+
+ uint64_t FileSize;
+ if (auto EC = sys::fs::file_size(DumpInput, FileSize))
+ return createStringError(EC, "Failed to get file size for '%s'.",
+ DumpInput.c_str());
+
+ std::error_code EC;
+ sys::fs::mapped_file_region MappedFile(
+ Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);
+
+ DataExtractor DE(StringRef(MappedFile.data(), MappedFile.size()), true, 8);
+ uint32_t OffsetPtr = 0;
+
+ auto FileHeaderOrError = readBinaryFormatHeader(DE, OffsetPtr);
+ if (!FileHeaderOrError)
+ return FileHeaderOrError.takeError();
+ auto &H = FileHeaderOrError.get();
+
+ FileBasedRecordProducer P(H, DE, OffsetPtr);
+
+ RecordPrinter RP(outs(), "\n");
+ if (!DumpVerify) {
+ PipelineConsumer C({&RP});
+ while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) {
+ auto R = P.produce();
+ if (!R)
+ return R.takeError();
+ if (auto E = C.consume(std::move(R.get())))
+ return E;
+ }
+ return Error::success();
+ }
+
+ BlockPrinter BP(outs(), RP);
+ std::vector<std::unique_ptr<Record>> Records;
+ LogBuilderConsumer C(Records);
+ while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) {
+ auto R = P.produce();
+ if (!R) {
+ // Print records we've found so far.
+ for (auto &Ptr : Records)
+ if (auto E = Ptr->apply(RP))
+ return joinErrors(std::move(E), R.takeError());
+ return R.takeError();
+ }
+ if (auto E = C.consume(std::move(R.get())))
+ return E;
+ }
+
+ // Once we have a trace, we then index the blocks.
+ BlockIndexer::Index Index;
+ BlockIndexer BI(Index);
+ for (auto &Ptr : Records)
+ if (auto E = Ptr->apply(BI))
+ return E;
+
+ if (auto E = BI.flush())
+ return E;
+
+ // Then we validate while printing each block.
+ BlockVerifier BV;
+ for (auto ProcessThreadBlocks : Index) {
+ auto &Blocks = ProcessThreadBlocks.second;
+ for (auto &B : Blocks) {
+ for (auto *R : B.Records) {
+ if (auto E = R->apply(BV))
+ return E;
+ if (auto E = R->apply(BP))
+ return E;
+ }
+ BV.reset();
+ BP.reset();
+ }
+ }
+ outs().flush();
+ return Error::success();
+});
diff --git a/tools/llvm-xray/xray-graph.cpp b/tools/llvm-xray/xray-graph.cpp
index c619bf86299b..fe49cca20d57 100644
--- a/tools/llvm-xray/xray-graph.cpp
+++ b/tools/llvm-xray/xray-graph.cpp
@@ -246,6 +246,10 @@ Error GraphRenderer::accountRecord(const XRayRecord &Record) {
updateStat(G[Record.FuncId].S, D);
break;
}
+ case RecordTypes::CUSTOM_EVENT:
+ case RecordTypes::TYPED_EVENT:
+ // TODO: Support custom and typed events in the graph processing?
+ break;
}
return Error::success();
diff --git a/tools/llvm-xray/xray-stacks.cpp b/tools/llvm-xray/xray-stacks.cpp
index 1a6069780a31..d3af9e25e6f2 100644
--- a/tools/llvm-xray/xray-stacks.cpp
+++ b/tools/llvm-xray/xray-stacks.cpp
@@ -366,6 +366,9 @@ public:
AccountRecordState *state) {
auto &TS = ThreadStackMap[R.TId];
switch (R.Type) {
+ case RecordTypes::CUSTOM_EVENT:
+ case RecordTypes::TYPED_EVENT:
+ return AccountRecordStatus::OK;
case RecordTypes::ENTER:
case RecordTypes::ENTER_ARG: {
state->wasLastRecordExit = false;
@@ -734,7 +737,7 @@ static CommandRegistration Unused(&Stack, []() -> Error {
Twine("Failed loading input file '") + Filename + "'",
std::make_error_code(std::errc::invalid_argument)),
TraceOrErr.takeError());
- logAllUnhandledErrors(TraceOrErr.takeError(), errs(), "");
+ logAllUnhandledErrors(TraceOrErr.takeError(), errs());
continue;
}
auto &T = *TraceOrErr;
diff --git a/tools/llvm-yaml-numeric-parser-fuzzer/CMakeLists.txt b/tools/llvm-yaml-numeric-parser-fuzzer/CMakeLists.txt
new file mode 100644
index 000000000000..34027431697f
--- /dev/null
+++ b/tools/llvm-yaml-numeric-parser-fuzzer/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ FuzzMutate
+)
+
+add_llvm_fuzzer(llvm-yaml-numeric-parser-fuzzer
+ yaml-numeric-parser-fuzzer.cpp
+ DUMMY_MAIN DummyYAMLNumericParserFuzzer.cpp
+ )
diff --git a/tools/llvm-yaml-numeric-parser-fuzzer/DummyYAMLNumericParserFuzzer.cpp b/tools/llvm-yaml-numeric-parser-fuzzer/DummyYAMLNumericParserFuzzer.cpp
new file mode 100644
index 000000000000..3396168acec0
--- /dev/null
+++ b/tools/llvm-yaml-numeric-parser-fuzzer/DummyYAMLNumericParserFuzzer.cpp
@@ -0,0 +1,19 @@
+//===--- DummyYAMLNumericParserFuzzer.cpp ---------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of main so we can build and test without linking libFuzzer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/FuzzMutate/FuzzerCLI.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+int main(int argc, char *argv[]) {
+ return llvm::runFuzzerOnInputs(argc, argv, LLVMFuzzerTestOneInput);
+}
diff --git a/tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp b/tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp
new file mode 100644
index 000000000000..9134d7628ec9
--- /dev/null
+++ b/tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp
@@ -0,0 +1,47 @@
+//===--- special-case-list-fuzzer.cpp - Fuzzer for special case lists -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <cassert>
+#include <string>
+
+llvm::Regex Infinity("^[-+]?(\\.inf|\\.Inf|\\.INF)$");
+llvm::Regex Base8("^0o[0-7]+$");
+llvm::Regex Base16("^0x[0-9a-fA-F]+$");
+llvm::Regex Float("^[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?$");
+
+inline bool isNumericRegex(llvm::StringRef S) {
+
+ if (S.equals(".nan") || S.equals(".NaN") || S.equals(".NAN"))
+ return true;
+
+ if (Infinity.match(S))
+ return true;
+
+ if (Base8.match(S))
+ return true;
+
+ if (Base16.match(S))
+ return true;
+
+ if (Float.match(S))
+ return true;
+
+ return false;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ std::string Input(reinterpret_cast<const char *>(Data), Size);
+ Input.erase(std::remove(Input.begin(), Input.end(), 0), Input.end());
+ if (!Input.empty() && llvm::yaml::isNumeric(Input) != isNumericRegex(Input))
+ LLVM_BUILTIN_TRAP;
+ return 0;
+}
diff --git a/tools/lto/lto.cpp b/tools/lto/lto.cpp
index f9b518ca039b..0a3ba98957e7 100644
--- a/tools/lto/lto.cpp
+++ b/tools/lto/lto.cpp
@@ -591,6 +591,13 @@ void thinlto_codegen_set_cache_size_bytes(
return unwrap(cg)->setCacheMaxSizeBytes(MaxSizeBytes);
}
+void thinlto_codegen_set_cache_size_megabytes(
+ thinlto_code_gen_t cg, unsigned MaxSizeMegabytes) {
+ uint64_t MaxSizeBytes = MaxSizeMegabytes;
+ MaxSizeBytes *= 1024 * 1024;
+ return unwrap(cg)->setCacheMaxSizeBytes(MaxSizeBytes);
+}
+
void thinlto_codegen_set_cache_size_files(
thinlto_code_gen_t cg, unsigned MaxSizeFiles) {
return unwrap(cg)->setCacheMaxSizeFiles(MaxSizeFiles);
diff --git a/tools/lto/lto.exports b/tools/lto/lto.exports
index abde3894cb20..cb881d104fb2 100644
--- a/tools/lto/lto.exports
+++ b/tools/lto/lto.exports
@@ -46,6 +46,7 @@ LLVMCreateDisasmCPU
LLVMDisasmDispose
LLVMDisasmInstruction
LLVMSetDisasmOptions
+LLVMCreateDisasmCPUFeatures
thinlto_create_codegen
thinlto_codegen_dispose
thinlto_codegen_add_module
@@ -58,6 +59,7 @@ thinlto_codegen_set_cache_pruning_interval
thinlto_codegen_set_cache_entry_expiration
thinlto_codegen_set_final_cache_size_relative_to_available_space
thinlto_codegen_set_cache_size_bytes
+thinlto_codegen_set_cache_size_megabytes
thinlto_codegen_set_cache_size_files
thinlto_codegen_set_savetemps_dir
thinlto_codegen_set_cpu
diff --git a/tools/msbuild/.gitignore b/tools/msbuild/.gitignore
index 01c1f5a9d0b9..692d171a21a0 100644
--- a/tools/msbuild/.gitignore
+++ b/tools/msbuild/.gitignore
@@ -1,2 +1,6 @@
-bin
-obj
+obj/
+bin/
+.vs/
+Key.snk
+packages/
+*.csproj.user
diff --git a/tools/msbuild/LLVM.Cpp.Common.props b/tools/msbuild/LLVM.Cpp.Common.props
index ffc1270a85c9..3420b77cfffa 100644
--- a/tools/msbuild/LLVM.Cpp.Common.props
+++ b/tools/msbuild/LLVM.Cpp.Common.props
@@ -1,75 +1,77 @@
-<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <!-- The general order of executing an MSBuild file is roughly:
- 1) vcxproj file
- 2) ├─ Import Microsoft.Cpp.props
- 3) │ └─ Import Toolset specific props (e.g. $(VCTargets)Platforms\Win32\PlatformToolsets\llvm\Toolset.props)
- 4) │ └─ Import This File (LLVM.Cpp.Common.props)
- 5) │─ Core logic of vcxproj (define files, override properties, etc)
- 6) └─ Import Microsoft.Cpp.targets
- 7) │─ Import Toolset specific targets file (e.g. $(VCTargets)Platforms\Win32\PlatformToolsets\llvm\Toolset.targets)
- 8) └─ Run the compiler.
- The important thing is that we have hooks at 3, 4, and 7. 3 and 4 give
- us the ability to provide initial values for toolchain settings (where
- is the compiler, what values are considered "default" for a given
- setting, etc), 7 gives us the ability to act on anything that the user
- has overridden (such as warning or erroring on incompatible settings,
- mapping settings to other settings, etc).
- -->
-
- <PropertyGroup>
- <!-- This initializes the values in Properties > General > Output Directory.
- Builds will fail without this. -->
- <OutDirWasSpecified Condition=" '$(OutDir)'!='' AND '$(OutDirWasSpecified)'=='' ">true</OutDirWasSpecified>
- <OutDirWasSpecified Condition=" '$(OutDir)'=='' AND '$(OutDirWasSpecified)'=='' ">false</OutDirWasSpecified>
-
- <IntDir Condition="'$(IntDir)'=='' AND '$(IntermediateOutputPath)'!=''">$(IntermediateOutputPath)</IntDir>
- <IntDir Condition="'$(IntDir)'=='' AND '$(IntermediateOutputPath)'==''">$(Configuration)\</IntDir>
- <OutDir Condition="'$(OutDir)'=='' AND '$(SolutionDir)' != ''">$(SolutionDir)$(Configuration)\</OutDir>
- <OutDir Condition="'$(OutDir)'=='' AND '$(SolutionDir)' == ''">$(IntDir)</OutDir>
- <DebuggerFlavor Condition="'$(DebuggerFlavor)'==''">WindowsLocalDebugger</DebuggerFlavor>
- </PropertyGroup>
-
- <PropertyGroup>
- <!-- Short names for platform toolsets (added to project name in Solution Explorer) -->
- <_PlatformToolsetShortNameFor_llvm>LLVM</_PlatformToolsetShortNameFor_llvm>
- <_PlatformToolsetFriendlyNameFor_llvm>LLVM</_PlatformToolsetFriendlyNameFor_llvm>
- </PropertyGroup>
-
- <!-- Find an installed LLVM and set up our paths. -->
- <PropertyGroup>
- <LLVMInstallDir>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\LLVM@LLVM)</LLVMInstallDir>
- <LLVMInstallDir Condition="'$(LLVMInstallDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\LLVM@LLVM)</LLVMInstallDir>
- <ClangClExecutable>$(LLVMInstallDir)bin\clang-cl.exe</ClangClExecutable>
- </PropertyGroup>
-
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.WindowsSDK.props" Condition="Exists('$(VCTargetsPath)\Microsoft.Cpp.WindowsSDK.props')"/>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Common.props" />
-
- <PropertyGroup>
- <!-- Set some paths (such as include paths) that are common to all platforms. This is the same as what
- the default paths for cl will use.
- -->
- <IncludePath Condition="'$(IncludePath)' == ''">$(IncludePath);$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
- <LibraryWPath Condition="'$(LibraryWPath)' == ''">$(WindowsSDK_MetadataPath);</LibraryWPath>
- <SourcePath Condition="'$(SourcePath)' == ''">$(VC_SourcePath);</SourcePath>
- </PropertyGroup>
-
-
- <!-- Set values which are reflected in the property UI by default. The user can override these
- by editing the vcxproj file (or making changes via the UI, which has the same effect).
- -->
- <ItemDefinitionGroup>
- <ClCompile>
- <!-- Set this to "Default" (which means not passing any /RTC option) so that any other value will
- be treated as having been overridden by the user. This Serves as a hint to the user that
- Default is the value we support, and other values will generate a warning. It also means
- that if the user simply creates a new project in MSVC (which uses /RTCu by default), then
- switches the toolset to Clang, we will still treat the value as default (which for us is to
- not pass the option). Only if the user explicitly overrode this setting in a project to use
- /RTCu would we see the warning. -->
- <BasicRuntimeChecks>Default</BasicRuntimeChecks>
-
- <AdditionalOptions>-m$(PlatformArchitecture) %(AdditionalOptions)</AdditionalOptions>
- </ClCompile>
- </ItemDefinitionGroup>
-</Project>
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!-- The general order of executing an MSBuild file is roughly:
+ 1) vcxproj file
+ 2) ├─ Import Microsoft.Cpp.props
+ 3) │ └─ Import Toolset specific props (e.g. $(VCTargets)Platforms\Win32\PlatformToolsets\llvm\Toolset.props)
+ 4) │ └─ Import This File (LLVM.Cpp.Common.props)
+ 5) │─ Core logic of vcxproj (define files, override properties, etc)
+ 6) └─ Import Microsoft.Cpp.targets
+ 7) │─ Import Toolset specific targets file (e.g. $(VCTargets)Platforms\Win32\PlatformToolsets\llvm\Toolset.targets)
+ 8) └─ Run the compiler.
+ The important thing is that we have hooks at 3, 4, and 7. 3 and 4 give
+ us the ability to provide initial values for toolchain settings (where
+ is the compiler, what values are considered "default" for a given
+ setting, etc), 7 gives us the ability to act on anything that the user
+ has overridden (such as warning or erroring on incompatible settings,
+ mapping settings to other settings, etc).
+ -->
+
+ <PropertyGroup>
+ <!-- This initializes the values in Properties > General > Output Directory.
+ Builds will fail without this. -->
+ <OutDirWasSpecified Condition=" '$(OutDir)'!='' AND '$(OutDirWasSpecified)'=='' ">true</OutDirWasSpecified>
+ <OutDirWasSpecified Condition=" '$(OutDir)'=='' AND '$(OutDirWasSpecified)'=='' ">false</OutDirWasSpecified>
+
+ <IntDir Condition="'$(IntDir)'=='' AND '$(IntermediateOutputPath)'!=''">$(IntermediateOutputPath)</IntDir>
+ <IntDir Condition="'$(IntDir)'=='' AND '$(IntermediateOutputPath)'==''">$(Configuration)\</IntDir>
+ <OutDir Condition="'$(OutDir)'=='' AND '$(SolutionDir)' != ''">$(SolutionDir)$(Configuration)\</OutDir>
+ <OutDir Condition="'$(OutDir)'=='' AND '$(SolutionDir)' == ''">$(IntDir)</OutDir>
+ <DebuggerFlavor Condition="'$(DebuggerFlavor)'==''">WindowsLocalDebugger</DebuggerFlavor>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <!-- Short names for platform toolsets (added to project name in Solution Explorer) -->
+ <_PlatformToolsetShortNameFor_llvm>LLVM</_PlatformToolsetShortNameFor_llvm>
+ <_PlatformToolsetFriendlyNameFor_llvm>LLVM</_PlatformToolsetFriendlyNameFor_llvm>
+ </PropertyGroup>
+
+ <!-- Find an installed LLVM and set up our paths. -->
+ <PropertyGroup>
+ <LLVMInstallDir>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\LLVM\LLVM)</LLVMInstallDir>
+ <LLVMInstallDir Condition="'$(LLVMInstallDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\LLVM\LLVM)</LLVMInstallDir>
+ <LLVMInstallDir Condition="'$(LLVMInstallDir)' != ''">$(LLVMInstallDir)\</LLVMInstallDir>
+ <ClangClExecutable>$(LLVMInstallDir)bin\clang-cl.exe</ClangClExecutable>
+ <LldLinkExecutable>$(LLVMInstallDir)bin\lld-link.exe</LldLinkExecutable>
+ <UseClangCl>true</UseClangCl>
+ <UseLldLink>true</UseLldLink>
+ </PropertyGroup>
+
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.WindowsSDK.props" Condition="Exists('$(VCTargetsPath)\Microsoft.Cpp.WindowsSDK.props')"/>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Common.props" />
+
+ <PropertyGroup>
+ <!-- Set some paths (such as include paths) that are common to all platforms. This is the same as what
+ the default paths for cl will use.
+ -->
+ <IncludePath Condition="'$(IncludePath)' == ''">$(IncludePath);$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
+ <LibraryWPath Condition="'$(LibraryWPath)' == ''">$(WindowsSDK_MetadataPath);</LibraryWPath>
+ <SourcePath Condition="'$(SourcePath)' == ''">$(VC_SourcePath);</SourcePath>
+ </PropertyGroup>
+
+
+ <!-- Set values which are reflected in the property UI by default. The user can override these
+ by editing the vcxproj file (or making changes via the UI, which has the same effect).
+ -->
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <!-- Set this to "Default" (which means not passing any /RTC option) so that any other value will
+ be treated as having been overridden by the user. This Serves as a hint to the user that
+ Default is the value we support, and other values will generate a warning. It also means
+ that if the user simply creates a new project in MSVC (which uses /RTCu by default), then
+ switches the toolset to Clang, we will still treat the value as default (which for us is to
+ not pass the option). Only if the user explicitly overrode this setting in a project to use
+ /RTCu would we see the warning. -->
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ </ClCompile>
+ </ItemDefinitionGroup>
+</Project>
diff --git a/tools/msbuild/LLVM.Cpp.Common.targets b/tools/msbuild/LLVM.Cpp.Common.targets
index 1edc08d2a325..5870a3d4c594 100644
--- a/tools/msbuild/LLVM.Cpp.Common.targets
+++ b/tools/msbuild/LLVM.Cpp.Common.targets
@@ -1,184 +1,131 @@
-<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Project="$(VCTargetsPath)\Microsoft.CppCommon.targets" />
-
- <PropertyGroup>
- <!-- Set the path to clang-cl executable based on the value of the project-
- level setting. This has to be done in the .targets file since values
- selected via the settings UI appear in the vcxproj (which is imported
- before the targets file but after the props file) and we need the path
- that the user may have overridden in the UI. -->
- <CLToolExe>$(ClangClExecutable)</CLToolExe>
- </PropertyGroup>
-
- <ItemGroup>
- <PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\llvm-general.xml">
- <Context>Project</Context>
- </PropertyPageSchema>
- </ItemGroup>
-
- <!-- We hook up a target to run every time ClCompile is about to run, the
- purpose of which is to sanitize the command line before it gets passed to
- the compiler. Some options we silently discard, other options we warn on
- and then discard, and other options we generate a hard error.
-
- We try to keep hard errors to a minimum and reserve it for cases where
- the option implies fundamentally different assumptions about the way code
- should be compiled. This code would probably generate an error anyway,
- but at least this way we give the user a more useful message about what
- the actual problem is, rather than relying on some obscure compilation
- error.
-
- For any options that clang-cl discards, we would prefer to not even pass
- them in on the command line. So if a user starts with a cl projects and
- changes the toolset to clang, they could have set options such as /Gm
- (minimal rebuild), /sdl (Security Checks), etc. The ClCompile task would
- then notice this and pass these through to clang-cl.exe. Clang would of
- course ignore them, but in some cases (such as /Gm), they would generate
- -Wunused-command-line-argument warnings, so it's better if we can just
- strip them from the command line entirely. This also has the side
- benefit of making command lines shorter which is always nice when trying
- to look at the tool output.
- -->
- <Target Name="BeforeClCompile" BeforeTargets="ClCompile">
- <!-- Warn on /Zi and /ZI, then map them both to /Z7. -->
- <Warning Condition="'%(ClCompile.DebugInformationFormat)' == 'ProgramDatabase'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support /Zi (Program Database). The file will be compiled as if /Z7 (C7 Compatible Debug Info) had been passed. Update the Debug Information Format in project settings to silence this warning."/>
- <Warning Condition="'%(ClCompile.DebugInformationFormat)' == 'EditAndContinue'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support /ZI (Program Database for Edit and Continue). The file will be compiled as if /Z7 (C7 Compatible Debug Info) had been passed. Update the Debug Information Format in project settings to silence this warning."/>
-
- <!-- Warn if Fiber Safe Optimizations are enabled, and then ignore them. -->
- <Warning Condition="'%(ClCompile.EnableFiberSafeOptimizations)' == 'true'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support fiber safe optimizations (/GT). Disable this option in compatibility settings to silence this warning."/>
-
- <!-- Warn if Whole Program Optimization is enabled, and then ignore it. -->
- <Warning Condition="'%(ClCompile.WholeProgramOptimization)' == 'true'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support MSVC Link Time Optimization. Disable this option in compatibility settings to silence this warning."/>
-
- <!-- Warn if Ignore Standard Include Paths is non-empty, then ignore it. -->
- <Warning Condition="'%(ClCompile.IgnoreStandardIncludePath)' == 'true'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support Ignore Standard Include Path (/X). Disable this option in compatibility settings to silence this warning."/>
-
- <!-- Warn if Smaller Type Check is enabled, then ignore it.-->
- <Warning Condition="'%(ClCompile.SmallerTypeCheck)' == 'true'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support Smaller Type Check (/RTCc). Disable this option in compatibility settings to silence this warning."/>
-
- <!-- Warn if Runtime Checks are enabled, then ignore them.-->
- <Warning Condition="'%(ClCompile.BasicRuntimeChecks)' != 'Default'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support Basic Runtime Checks (/RTCu, /RTC1, /RTCs). Disable this option in compatibility settings to silence this warning."/>
-
- <!-- Warn if parallel code generation on #pragma loop is enabled, then ignore. -->
- <Warning Condition="'(ClCompile.EnableParallelCodeGeneration)' == 'true'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support parallel code generation with #pragma loop(hint) (/Qpar). Disable this option in compatibility settings to silence this warning."/>
-
- <!-- Warn if hotpatchable images are turned on -->
- <Warning Condition="'%(ClCompile.CreateHotpatchableImage)' == 'true'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support creating hotpatchable images (/hotpatch). Disable this option in compatibility settings to silence this warning."/>
-
- <!-- Warn if /Zc:forScope- is specified, and then ignore it. -->
- <Warning Condition="'%(ClCompile.ForceConformanceInForLoopScope)' == 'false'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support disabling for loop scope conformance (/Zc:forScope-). Disable this option in compatibility settings to silence this warning."/>
-
- <!-- Warn if /Zc:wchar_t- is specified, and then ignore it. -->
- <Warning Condition="'%(ClCompile.TreatWChar_tAsBuiltInType)' == 'false'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support treating wchar_t as a non builtin type (/Zc:wchar_t-). Disable this option in compatibility settings to silence this warning."/>
-
- <!-- Warn if XML Documentation is generated, and then ignore it. -->
- <Warning Condition="'%(ClCompile.GenerateXMLDocumentationFiles)' == 'true'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support generating xml documentation comment files (/doc). Disable this option in compatibility settings to silence this warning."/>
-
- <!-- Warn if Browse Information is generated, and then ignore it. -->
- <Warning Condition="'%(ClCompile.BrowseInformation)' == 'true'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support generating browse information (/FR). Disable this option in compatibility settings to silence this warning."/>
-
- <!-- Warn if /analyze is passed, then ignore it. -->
- <Warning Condition="'%(ClCompile.EnablePREfast)' == 'true'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support MSVC code analysis functionality (/analyze). Disable this option in compatibility settings to silence this warning."/>
-
- <!-- Error if they're trying to compile this file as managed code. -->
- <Error Condition="('%(ClCompile.CompileAsManaged)' != 'false') AND ('%(ClCompile.CompileAsManaged)' != '')"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support compiling managed code (/clr). This file cannot be compiled."/>
-
- <!-- Error if WinRT is being used. -->
- <Error Condition="('%(ClCompile.CompileAsWinRT)' == 'true') OR ('%(ClCompile.WinRTNoStdLib)' == 'true')"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support Windows Runtime Language Extensions (/ZW, /ZW:nostdlib). This file cannot be compiled."/>
-
- <!-- Error if OpenMP language extensions are enabled. -->
- <Error Condition="'%(ClCompile.OpenMPSupport)' == 'true'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support OpenMP (/openmp). This file cannot be compiled."/>
-
- <!-- Error if C++ Modules are enabled. Clang has its own notion of modules that are not compatible. -->
- <Error Condition="'%(ClCompile.EnableModules)' == 'true'"
- File="@(ClCompile)(0,0)"
- Text="clang-cl does not support MSVC Modules (/experimental:module). This file cannot be compiled."/>
-
- <ItemGroup>
- <ClCompile>
- <!-- Map /ZI and /Zi to /Z7. Clang internally does this, so if we were
- to just pass the option through, clang would work. The problem is
- that MSBuild would not. MSBuild detects /ZI and /Zi and then
- assumes (rightly) that there will be a compiler-generated PDB (e.g.
- vc141.pdb). Since clang-cl will not emit this, MSBuild will always
- think that the compiler-generated PDB needs to be re-generated from
- scratch and trigger a full build. The way to avoid this is to
- always give MSBuild accurate information about how we plan to
- generate debug info (which is to always using /Z7 semantics).
- -->
- <DebugInformationFormat Condition="'%(ClCompile.DebugInformationFormat)' == 'ProgramDatabase'">OldStyle</DebugInformationFormat>
- <DebugInformationFormat Condition="'%(ClCompile.DebugInformationFormat)' == 'EditAndContinue'">OldStyle</DebugInformationFormat>
-
- <!-- Unset any options that we either silently ignore or warn about due to compatibility.
- Generally when an option is set to no value, that means "Don't pass an option to the
- compiler at all."
- -->
- <WholeProgramOptimization/>
- <EnableFiberSafeOptimizations/>
- <IgnoreStandardIncludePath/>
- <EnableParallelCodeGeneration/>
- <ForceConformanceInForLoopScope/>
- <TreatWChar_tAsBuiltInType/>
- <SDLCheck/>
- <GenerateXMLDocumentationFiles/>
- <BrowseInformation/>
- <EnablePREfast/>
- <MinimalRebuild/>
- <StringPooling/>
- <ExpandAttributedSource/>
- <EnforceTypeConversionRules/>
- <ErrorReporting/>
- <DisableLanguageExtensions/>
- <ProgramDataBaseFileName/>
- <DisableSpecificWarnings/>
- <TreatSpecificWarningsAsErrors/>
- <ForcedUsingFiles/>
- <PREfastLog/>
- <PREfastAdditionalOptions/>
- <PREfastAdditionalPlugins/>
- <MultiProcessorCompilation/>
- <UseFullPaths/>
- <RemoveUnreferencedCodeData/>
-
- <!-- We can't just unset BasicRuntimeChecks, as that will pass /RTCu to the compiler.
- We have to explicitly set it to 'Default' to avoid passing anything. -->
- <BasicRuntimeChecks>Default</BasicRuntimeChecks>
- </ClCompile>
- </ItemGroup>
- </Target>
-
-</Project>
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(VCTargetsPath)\Microsoft.CppCommon.targets" />
+
+ <PropertyGroup>
+ <!-- Set the path to clang-cl executable based on the value of the project-
+ level setting. This has to be done in the .targets file since values
+ selected via the settings UI appear in the vcxproj (which is imported
+ before the targets file but after the props file) and we need the path
+ that the user may have overridden in the UI. -->
+ <CLToolExe Condition="$(UseClangCl)">$(ClangClExecutable)</CLToolExe>
+ <LinkToolExe Condition="$(UseLldLink)">$(LldLinkExecutable)</LinkToolExe>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\llvm-general.xml">
+ <Context>Project</Context>
+ </PropertyPageSchema>
+ </ItemGroup>
+
+ <!-- Take any clang-specific options that the user wants to pass and stick them onto the
+ general purpose list of command line flags. -->
+ <ItemDefinitionGroup Condition="$(UseClangCl)">
+ <ClCompile>
+ <AdditionalOptions>-m$(PlatformArchitecture) $(ClangClAdditionalOptions) %(AdditionalOptions)</AdditionalOptions>
+ </ClCompile>
+ </ItemDefinitionGroup>
+
+ <ItemDefinitionGroup Condition="$(UseLldLink)">
+ <Link>
+ <AdditionalOptions>$(LldLinkAdditionalOptions) %(AdditionalOptions)</AdditionalOptions>
+ </Link>
+ </ItemDefinitionGroup>
+
+ <!-- We hook up a target to run every time ClCompile is about to run, the
+ purpose of which is to sanitize the command line before it gets passed to
+ the compiler. Some options we silently discard, other options we warn on
+ and then discard, and other options we generate a hard error.
+
+ We try to keep hard errors to a minimum and reserve it for cases where
+ the option implies fundamentally different assumptions about the way code
+ should be compiled. This code would probably generate an error anyway,
+ but at least this way we give the user a more useful message about what
+ the actual problem is, rather than relying on some obscure compilation
+ error.
+
+ For any options that clang-cl discards, we would prefer to not even pass
+ them in on the command line. So if a user starts with a cl projects and
+ changes the toolset to clang, they could have set options such as /Gm
+ (minimal rebuild), /sdl (Security Checks), etc. The ClCompile task would
+ then notice this and pass these through to clang-cl.exe. Clang would of
+ course ignore them, but in some cases (such as /Gm), they would generate
+ -Wunused-command-line-argument warnings, so it's better if we can just
+ strip them from the command line entirely. This also has the side
+ benefit of making command lines shorter which is always nice when trying
+ to look at the tool output.
+ -->
+ <Target Name="BeforeClCompile" BeforeTargets="ClCompile" Condition="$(UseClangCl)">
+ <!-- Error if they're trying to compile this file as managed code. -->
+ <Error Condition="('%(ClCompile.CompileAsManaged)' != 'false') AND ('%(ClCompile.CompileAsManaged)' != '')"
+ File="@(ClCompile)(0,0)"
+ Text="clang-cl does not support compiling managed code (/clr). This file cannot be compiled."/>
+
+ <!-- Error if WinRT is being used. -->
+ <Error Condition="('%(ClCompile.CompileAsWinRT)' == 'true') OR ('%(ClCompile.WinRTNoStdLib)' == 'true')"
+ File="@(ClCompile)(0,0)"
+ Text="clang-cl does not support Windows Runtime Language Extensions (/ZW, /ZW:nostdlib). This file cannot be compiled."/>
+
+ <!-- Error if OpenMP language extensions are enabled. -->
+ <Error Condition="'%(ClCompile.OpenMPSupport)' == 'true'"
+ File="@(ClCompile)(0,0)"
+ Text="clang-cl does not support OpenMP (/openmp). This file cannot be compiled."/>
+
+ <!-- Error if C++ Modules are enabled. Clang has its own notion of modules that are not compatible. -->
+ <Error Condition="'%(ClCompile.EnableModules)' == 'true'"
+ File="@(ClCompile)(0,0)"
+ Text="clang-cl does not support MSVC Modules (/experimental:module). This file cannot be compiled."/>
+
+ <ItemGroup>
+ <ClCompile>
+ <!-- Map /ZI and /Zi to /Z7. Clang internally does this, so if we were
+ to just pass the option through, clang would work. The problem is
+ that MSBuild would not. MSBuild detects /ZI and /Zi and then
+ assumes (rightly) that there will be a compiler-generated PDB (e.g.
+ vc141.pdb). Since clang-cl will not emit this, MSBuild will always
+ think that the compiler-generated PDB needs to be re-generated from
+ scratch and trigger a full build. The way to avoid this is to
+ always give MSBuild accurate information about how we plan to
+ generate debug info (which is to always using /Z7 semantics).
+ -->
+ <DebugInformationFormat Condition="'%(ClCompile.DebugInformationFormat)' == 'ProgramDatabase'">OldStyle</DebugInformationFormat>
+ <DebugInformationFormat Condition="'%(ClCompile.DebugInformationFormat)' == 'EditAndContinue'">OldStyle</DebugInformationFormat>
+
+ <!-- Unset any options that we either silently ignore or warn about due to compatibility.
+ Generally when an option is set to no value, that means "Don't pass an option to the
+ compiler at all."
+ -->
+ <WholeProgramOptimization/>
+ <EnableFiberSafeOptimizations/>
+ <IgnoreStandardIncludePath/>
+ <EnableParallelCodeGeneration/>
+ <ForceConformanceInForLoopScope/>
+ <TreatWChar_tAsBuiltInType/>
+ <SDLCheck/>
+ <GenerateXMLDocumentationFiles/>
+ <BrowseInformation/>
+ <EnablePREfast/>
+ <MinimalRebuild/>
+ <StringPooling/>
+ <ExpandAttributedSource/>
+ <EnforceTypeConversionRules/>
+ <ErrorReporting/>
+ <DisableLanguageExtensions/>
+ <ProgramDataBaseFileName/>
+ <DisableSpecificWarnings/>
+ <TreatSpecificWarningsAsErrors/>
+ <ForcedUsingFiles/>
+ <PREfastLog/>
+ <PREfastAdditionalOptions/>
+ <PREfastAdditionalPlugins/>
+ <MultiProcessorCompilation/>
+ <UseFullPaths/>
+ <RemoveUnreferencedCodeData/>
+
+ <!-- We can't just unset BasicRuntimeChecks, as that will pass /RTCu to the compiler.
+ We have to explicitly set it to 'Default' to avoid passing anything. -->
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ </ClCompile>
+ </ItemGroup>
+ </Target>
+
+</Project>
diff --git a/tools/msbuild/llvm-general.xml b/tools/msbuild/llvm-general.xml
index 3b72c1250da2..fc04374afb9b 100644
--- a/tools/msbuild/llvm-general.xml
+++ b/tools/msbuild/llvm-general.xml
@@ -9,17 +9,47 @@
<Category Name="General" DisplayName="General" Description="General" />
</Rule.Categories>
<Rule.DataSource>
- <DataSource Persistence="ProjectFile" Label="Configuration" />
+ <DataSource Persistence="ProjectFile" Label="LLVM" />
</Rule.DataSource>
+ <BoolProperty Name="UseClangCl"
+ DisplayName="Use clang-cl"
+ Description="Use clang-cl for compiling. If this option is disabled, the Microsoft compiler (cl.exe) will be used instead."
+ Category="General"
+ Default="true">
+ </BoolProperty>
<StringProperty Name="ClangClExecutable"
- DisplayName="Clang-CL Executable"
+ DisplayName="clang-cl Executable"
Description="Specifies the path to clang-cl.exe."
Category="General"
Default="$(LLVMInstallDir)bin\clang-cl.exe"
Subtype="file">
- <StringProperty.DataSource>
- <DataSource Persistence="ProjectFile" Label="" />
- </StringProperty.DataSource>
</StringProperty>
+
+ <StringProperty Name="ClangClAdditionalOptions"
+ DisplayName="Additional Compiler Options"
+ Description="Additional options to pass to clang. This is essentially the same as C/C++ > Command Line > Additional Options, except that it is safe to put options here that will be rejected by cl.exe in case you switch toolchains back and forth."
+ Category="General">
+ </StringProperty>
+
+ <BoolProperty Name="UseLldLink"
+ DisplayName="Use lld-link"
+ Description="Use lld-link for linking. If this option is disabled, the Microsoft linker (link.exe) will be used instead."
+ Category="General"
+ Default="true">
+ </BoolProperty>
+ <StringProperty Name="LldLinkExecutable"
+ DisplayName="lld-link Executable"
+ Description="Specifies the path to lld-link.exe."
+ Category="General"
+ Default="$(LLVMInstallDir)bin\lld-link.exe"
+ Subtype="file">
+ </StringProperty>
+
+ <StringProperty Name="LldLinkAdditionalOptions"
+ DisplayName="Additional Linker Options"
+ Description="Additional options to pass to lld-link. This is essentially the same as General > Linker > Command Line > Additional Options, except that it is safe to put options here that will be rejected by link.exe in case you switch toolchains back and forth."
+ Category="General">
+ </StringProperty>
+
</Rule>
diff --git a/tools/msbuild/llvm.csproj b/tools/msbuild/llvm.csproj
index a614bb2f22ba..39e898d73682 100644
--- a/tools/msbuild/llvm.csproj
+++ b/tools/msbuild/llvm.csproj
@@ -64,6 +64,7 @@
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
+ <None Include="Key.snk" />
<Content Include="Platformx64\Toolset.props">
<IncludeInVSIX>true</IncludeInVSIX>
<InstallRoot>VCTargets</InstallRoot>
@@ -90,11 +91,7 @@
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
- <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
- Other similar extension points exist, see Microsoft.Common.targets.
- <Target Name="BeforeBuild">
- </Target>
- <Target Name="AfterBuild">
- </Target>
- -->
+ <PropertyGroup>
+ <PreBuildEvent>if not exist $(ProjectDir)Key.snk ("$(TargetFrameworkSDKToolsDirectory)\x64\sn.exe" -k $(ProjectDir)Key.snk)</PreBuildEvent>
+ </PropertyGroup>
</Project> \ No newline at end of file
diff --git a/tools/msbuild/source.extension.vsixmanifest b/tools/msbuild/source.extension.vsixmanifest
index 28be6a4242c2..c5b778c7ba1d 100644
--- a/tools/msbuild/source.extension.vsixmanifest
+++ b/tools/msbuild/source.extension.vsixmanifest
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
- <Identity Id="llvm.d29c51f0-961f-4e20-aad6-356af569907f" Version="1.0" Language="en-US" Publisher="The LLVM Foundation" />
+ <Identity Id="llvm.d29c51f0-961f-4e20-aad6-356af569907f" Version="1.0.340780" Language="en-US" Publisher="LLVM Extensions" />
<DisplayName>LLVM Compiler Toolchain</DisplayName>
<Description xml:space="preserve">Allows the LLVM Compiler Toolchain (installed separately) to be used from within Visual Studio to build C/C++ Projects.</Description>
<License>license.txt</License>
</Metadata>
<Installation AllUsers="true">
- <InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[14.0,16.0)" />
- <InstallationTarget Version="[14.0,16.0)" Id="Microsoft.VisualStudio.Pro" />
- <InstallationTarget Version="[14.0,16.0)" Id="Microsoft.VisualStudio.Enterprise" />
+ <InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[15.0,16.0)" />
+ <InstallationTarget Version="[15.0,16.0)" Id="Microsoft.VisualStudio.Pro" />
+ <InstallationTarget Version="[15.0,16.0)" Id="Microsoft.VisualStudio.Enterprise" />
</Installation>
<Dependencies>
<Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.5,)" />
diff --git a/tools/obj2yaml/Error.cpp b/tools/obj2yaml/Error.cpp
index 399b563fd9c9..2b6a815fe2f5 100644
--- a/tools/obj2yaml/Error.cpp
+++ b/tools/obj2yaml/Error.cpp
@@ -53,7 +53,7 @@ const std::error_category &obj2yaml_category() {
char Obj2YamlError::ID = 0;
-void Obj2YamlError::log(raw_ostream &OS) const { OS << ErrMsg << "\n"; }
+void Obj2YamlError::log(raw_ostream &OS) const { OS << ErrMsg; }
std::error_code Obj2YamlError::convertToErrorCode() const {
return std::error_code(static_cast<int>(Code), obj2yaml_category());
diff --git a/tools/obj2yaml/coff2yaml.cpp b/tools/obj2yaml/coff2yaml.cpp
index e7e9e19fd3dc..3b44780fac57 100644
--- a/tools/obj2yaml/coff2yaml.cpp
+++ b/tools/obj2yaml/coff2yaml.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "obj2yaml.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h"
#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h"
#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h"
@@ -142,12 +143,24 @@ void COFFDumper::dumpSections(unsigned NumSections) {
codeview::StringsAndChecksumsRef SC;
initializeFileAndStringTable(Obj, SC);
+ StringMap<bool> SymbolUnique;
+ for (const auto &S : Obj.symbols()) {
+ object::COFFSymbolRef Symbol = Obj.getCOFFSymbol(S);
+ StringRef Name;
+ Obj.getSymbolName(Symbol, Name);
+ StringMap<bool>::iterator It;
+ bool Inserted;
+ std::tie(It, Inserted) = SymbolUnique.insert(std::make_pair(Name, true));
+ if (!Inserted)
+ It->second = false;
+ }
+
for (const auto &ObjSection : Obj.sections()) {
const object::coff_section *COFFSection = Obj.getCOFFSection(ObjSection);
COFFYAML::Section NewYAMLSection;
ObjSection.getName(NewYAMLSection.Name);
NewYAMLSection.Header.Characteristics = COFFSection->Characteristics;
- NewYAMLSection.Header.VirtualAddress = ObjSection.getAddress();
+ NewYAMLSection.Header.VirtualAddress = COFFSection->VirtualAddress;
NewYAMLSection.Header.VirtualSize = COFFSection->VirtualSize;
NewYAMLSection.Header.NumberOfLineNumbers =
COFFSection->NumberOfLinenumbers;
@@ -188,11 +201,14 @@ void COFFDumper::dumpSections(unsigned NumSections) {
if (!SymbolNameOrErr) {
std::string Buf;
raw_string_ostream OS(Buf);
- logAllUnhandledErrors(SymbolNameOrErr.takeError(), OS, "");
+ logAllUnhandledErrors(SymbolNameOrErr.takeError(), OS);
OS.flush();
report_fatal_error(Buf);
}
- Rel.SymbolName = *SymbolNameOrErr;
+ if (SymbolUnique.lookup(*SymbolNameOrErr))
+ Rel.SymbolName = *SymbolNameOrErr;
+ else
+ Rel.SymbolTableIndex = reloc->SymbolTableIndex;
Rel.VirtualAddress = reloc->VirtualAddress;
Rel.Type = reloc->Type;
Relocations.push_back(Rel);
diff --git a/tools/obj2yaml/dwarf2yaml.cpp b/tools/obj2yaml/dwarf2yaml.cpp
index 91cec8b7c6ca..1ce6c036e6f5 100644
--- a/tools/obj2yaml/dwarf2yaml.cpp
+++ b/tools/obj2yaml/dwarf2yaml.cpp
@@ -81,8 +81,9 @@ void dumpDebugARanges(DWARFContext &DCtx, DWARFYAML::Data &Y) {
}
void dumpPubSection(DWARFContext &DCtx, DWARFYAML::PubSection &Y,
- StringRef Section) {
- DataExtractor PubSectionData(Section, DCtx.isLittleEndian(), 0);
+ DWARFSection Section) {
+ DWARFDataExtractor PubSectionData(DCtx.getDWARFObj(), Section,
+ DCtx.isLittleEndian(), 0);
uint32_t Offset = 0;
dumpInitialLength(PubSectionData, Offset, Y.Length);
Y.Version = PubSectionData.getU16(&Offset);
diff --git a/tools/obj2yaml/elf2yaml.cpp b/tools/obj2yaml/elf2yaml.cpp
index dea4d1b31827..48ecee02c4d0 100644
--- a/tools/obj2yaml/elf2yaml.cpp
+++ b/tools/obj2yaml/elf2yaml.cpp
@@ -114,6 +114,7 @@ template <class ELFT> ErrorOr<ELFYAML::Object *> ELFDumper<ELFT>::dump() {
Y->Header.Class = ELFYAML::ELF_ELFCLASS(Obj.getHeader()->getFileClass());
Y->Header.Data = ELFYAML::ELF_ELFDATA(Obj.getHeader()->getDataEncoding());
Y->Header.OSABI = Obj.getHeader()->e_ident[ELF::EI_OSABI];
+ Y->Header.ABIVersion = Obj.getHeader()->e_ident[ELF::EI_ABIVERSION];
Y->Header.Type = Obj.getHeader()->e_type;
Y->Header.Machine = Obj.getHeader()->e_machine;
Y->Header.Flags = Obj.getHeader()->e_flags;
diff --git a/tools/obj2yaml/obj2yaml.cpp b/tools/obj2yaml/obj2yaml.cpp
index 76786158d989..459572a57344 100644
--- a/tools/obj2yaml/obj2yaml.cpp
+++ b/tools/obj2yaml/obj2yaml.cpp
@@ -50,7 +50,7 @@ static void reportError(StringRef Input, Error Err) {
Input = "<stdin>";
std::string ErrMsg;
raw_string_ostream OS(ErrMsg);
- logAllUnhandledErrors(std::move(Err), OS, "");
+ logAllUnhandledErrors(std::move(Err), OS);
OS.flush();
errs() << "Error reading file: " << Input << ": " << ErrMsg;
errs().flush();
diff --git a/tools/obj2yaml/wasm2yaml.cpp b/tools/obj2yaml/wasm2yaml.cpp
index dbaf1a2848f5..7581bbef50ad 100644
--- a/tools/obj2yaml/wasm2yaml.cpp
+++ b/tools/obj2yaml/wasm2yaml.cpp
@@ -49,11 +49,23 @@ static WasmYAML::Limits make_limits(const wasm::WasmLimits &Limits) {
return L;
}
-std::unique_ptr<WasmYAML::CustomSection> WasmDumper::dumpCustomSection(const WasmSection &WasmSec) {
+std::unique_ptr<WasmYAML::CustomSection>
+WasmDumper::dumpCustomSection(const WasmSection &WasmSec) {
std::unique_ptr<WasmYAML::CustomSection> CustomSec;
- if (WasmSec.Name == "name") {
- std::unique_ptr<WasmYAML::NameSection> NameSec = make_unique<WasmYAML::NameSection>();
- for (const llvm::wasm::WasmFunctionName &Func: Obj.debugNames()) {
+ if (WasmSec.Name == "dylink") {
+ std::unique_ptr<WasmYAML::DylinkSection> DylinkSec =
+ make_unique<WasmYAML::DylinkSection>();
+ const wasm::WasmDylinkInfo& Info = Obj.dylinkInfo();
+ DylinkSec->MemorySize = Info.MemorySize;
+ DylinkSec->MemoryAlignment = Info.MemoryAlignment;
+ DylinkSec->TableSize = Info.TableSize;
+ DylinkSec->TableAlignment = Info.TableAlignment;
+ DylinkSec->Needed = Info.Needed;
+ CustomSec = std::move(DylinkSec);
+ } else if (WasmSec.Name == "name") {
+ std::unique_ptr<WasmYAML::NameSection> NameSec =
+ make_unique<WasmYAML::NameSection>();
+ for (const llvm::wasm::WasmFunctionName &Func : Obj.debugNames()) {
WasmYAML::NameEntry NameEntry;
NameEntry.Name = Func.Name;
NameEntry.Index = Func.Index;
@@ -61,7 +73,8 @@ std::unique_ptr<WasmYAML::CustomSection> WasmDumper::dumpCustomSection(const Was
}
CustomSec = std::move(NameSec);
} else if (WasmSec.Name == "linking") {
- std::unique_ptr<WasmYAML::LinkingSection> LinkingSec = make_unique<WasmYAML::LinkingSection>();
+ std::unique_ptr<WasmYAML::LinkingSection> LinkingSec =
+ make_unique<WasmYAML::LinkingSection>();
LinkingSec->Version = Obj.linkingData().Version;
ArrayRef<StringRef> Comdats = Obj.linkingData().Comdats;
@@ -70,7 +83,7 @@ std::unique_ptr<WasmYAML::CustomSection> WasmDumper::dumpCustomSection(const Was
for (auto &Func : Obj.functions()) {
if (Func.Comdat != UINT32_MAX) {
LinkingSec->Comdats[Func.Comdat].Entries.emplace_back(
- WasmYAML::ComdatEntry{wasm::WASM_COMDAT_FUNCTION, Func.Index});
+ WasmYAML::ComdatEntry{wasm::WASM_COMDAT_FUNCTION, Func.Index});
}
}
@@ -104,6 +117,7 @@ std::unique_ptr<WasmYAML::CustomSection> WasmDumper::dumpCustomSection(const Was
break;
case wasm::WASM_SYMBOL_TYPE_FUNCTION:
case wasm::WASM_SYMBOL_TYPE_GLOBAL:
+ case wasm::WASM_SYMBOL_TYPE_EVENT:
Info.ElementIndex = Symbol.ElementIndex;
break;
case wasm::WASM_SYMBOL_TYPE_SECTION:
@@ -152,9 +166,13 @@ ErrorOr<WasmYAML::Object *> WasmDumper::dump() {
for (const auto &FunctionSig : Obj.types()) {
WasmYAML::Signature Sig;
Sig.Index = Index++;
- Sig.ReturnType = FunctionSig.ReturnType;
- for (const auto &ParamType : FunctionSig.ParamTypes)
- Sig.ParamTypes.push_back(ParamType);
+ Sig.ReturnType = wasm::WASM_TYPE_NORESULT;
+ assert(FunctionSig.Returns.size() <= 1 &&
+ "Functions with multiple returns are not supported");
+ if (FunctionSig.Returns.size())
+ Sig.ReturnType = static_cast<uint32_t>(FunctionSig.Returns[0]);
+ for (const auto &ParamType : FunctionSig.Params)
+ Sig.ParamTypes.push_back(static_cast<uint32_t>(ParamType));
TypeSec->Signatures.push_back(Sig);
}
S = std::move(TypeSec);
@@ -175,6 +193,10 @@ ErrorOr<WasmYAML::Object *> WasmDumper::dump() {
Im.GlobalImport.Type = Import.Global.Type;
Im.GlobalImport.Mutable = Import.Global.Mutable;
break;
+ case wasm::WASM_EXTERNAL_EVENT:
+ Im.EventImport.Attribute = Import.Event.Attribute;
+ Im.EventImport.SigIndex = Import.Event.SigIndex;
+ break;
case wasm::WASM_EXTERNAL_TABLE:
Im.TableImport = make_table(Import.Table);
break;
@@ -224,6 +246,18 @@ ErrorOr<WasmYAML::Object *> WasmDumper::dump() {
S = std::move(GlobalSec);
break;
}
+ case wasm::WASM_SEC_EVENT: {
+ auto EventSec = make_unique<WasmYAML::EventSection>();
+ for (auto &Event : Obj.events()) {
+ WasmYAML::Event E;
+ E.Index = Event.Index;
+ E.Attribute = Event.Type.Attribute;
+ E.SigIndex = Event.Type.SigIndex;
+ EventSec->Events.push_back(E);
+ }
+ S = std::move(EventSec);
+ break;
+ }
case wasm::WASM_SEC_START: {
auto StartSec = make_unique<WasmYAML::StartSection>();
StartSec->StartFunction = Obj.startFunction();
@@ -290,7 +324,7 @@ ErrorOr<WasmYAML::Object *> WasmDumper::dump() {
llvm_unreachable("Unknown section type");
break;
}
- for (const wasm::WasmRelocation &Reloc: WasmSec.Relocations) {
+ for (const wasm::WasmRelocation &Reloc : WasmSec.Relocations) {
WasmYAML::Relocation R;
R.Type = Reloc.Type;
R.Index = Reloc.Index;
diff --git a/tools/opt-remarks/CMakeLists.txt b/tools/opt-remarks/CMakeLists.txt
new file mode 100644
index 000000000000..a87beae1e893
--- /dev/null
+++ b/tools/opt-remarks/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(LLVM_LINK_COMPONENTS
+ OptRemarks
+ )
+
+set(SOURCES
+ liboptremarks.cpp
+ )
+
+set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/OptRemarks.exports)
+
+add_llvm_library(OptRemarks SHARED ${SOURCES})
+
+install(FILES ${LLVM_MAIN_INCLUDE_DIR}/llvm-c/OptRemarks.h
+ DESTINATION include/llvm-c
+ COMPONENT OptRemarks)
+
+if (APPLE)
+ set(OPTREMARKS_VERSION ${LLVM_VERSION_MAJOR})
+ set_property(TARGET OptRemarks APPEND_STRING PROPERTY
+ LINK_FLAGS
+ " -compatibility_version 1 -current_version ${OPTREMARKS_VERSION}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}")
+endif()
diff --git a/tools/opt-remarks/OptRemarks.exports b/tools/opt-remarks/OptRemarks.exports
new file mode 100644
index 000000000000..c3f678d754fc
--- /dev/null
+++ b/tools/opt-remarks/OptRemarks.exports
@@ -0,0 +1,6 @@
+LLVMOptRemarkParserCreate
+LLVMOptRemarkParserGetNext
+LLVMOptRemarkParserHasError
+LLVMOptRemarkParserGetErrorMessage
+LLVMOptRemarkParserDispose
+LLVMOptRemarkVersion
diff --git a/tools/llvm-mca/HWEventListener.cpp b/tools/opt-remarks/liboptremarks.cpp
index f27a04a9a980..13acada06ac2 100644
--- a/tools/llvm-mca/HWEventListener.cpp
+++ b/tools/opt-remarks/liboptremarks.cpp
@@ -1,4 +1,4 @@
-//===----------------------- HWEventListener.cpp ----------------*- C++ -*-===//
+//===-liboptremarks.cpp - LLVM Opt-Remarks Shared Library -----------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -6,16 +6,13 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-/// \file
-///
-/// This file defines a vtable anchor for class HWEventListener.
-///
+//
+// Provide a library to work with optimization remarks.
+//
//===----------------------------------------------------------------------===//
-#include "HWEventListener.h"
-
-namespace mca {
+#include "llvm-c/OptRemarks.h"
-// Anchor the vtable here.
-void HWEventListener::anchor() {}
-} // namespace mca
+extern uint32_t LLVMOptRemarkVersion(void) {
+ return OPT_REMARKS_API_VERSION;
+}
diff --git a/tools/opt-viewer/opt-diff.py b/tools/opt-viewer/opt-diff.py
index f3bfd1860b91..36e81a5d569a 100755
--- a/tools/opt-viewer/opt-diff.py
+++ b/tools/opt-viewer/opt-diff.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
from __future__ import print_function
diff --git a/tools/opt-viewer/opt-stats.py b/tools/opt-viewer/opt-stats.py
index 03de23bdb275..f4ee3a7d44e6 100755
--- a/tools/opt-viewer/opt-stats.py
+++ b/tools/opt-viewer/opt-stats.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
from __future__ import print_function
diff --git a/tools/opt-viewer/opt-viewer.py b/tools/opt-viewer/opt-viewer.py
index 4887043e0f96..f6582506311c 100755
--- a/tools/opt-viewer/opt-viewer.py
+++ b/tools/opt-viewer/opt-viewer.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
from __future__ import print_function
@@ -72,7 +72,10 @@ class SourceFileRenderer:
file_text = stream.read()
if self.no_highlight:
- html_highlighted = file_text.decode('utf-8')
+ if sys.version_info.major >= 3:
+ html_highlighted = file_text
+ else:
+ html_highlighted = file_text.decode('utf-8')
else:
html_highlighted = highlight(
file_text,
diff --git a/tools/opt-viewer/optpmap.py b/tools/opt-viewer/optpmap.py
index db6b079b3a6d..ff3e683f3d06 100644
--- a/tools/opt-viewer/optpmap.py
+++ b/tools/opt-viewer/optpmap.py
@@ -42,7 +42,7 @@ def pmap(func, iterable, processes, should_print_progress, *args, **kwargs):
func_and_args = [(func, arg, should_print_progress,) for arg in iterable]
if processes == 1:
- result = map(_wrapped_func, func_and_args, *args, **kwargs)
+ result = list(map(_wrapped_func, func_and_args, *args, **kwargs))
else:
pool = multiprocessing.Pool(initializer=_init,
initargs=(_current, _total,),
diff --git a/tools/opt-viewer/optrecord.py b/tools/opt-viewer/optrecord.py
index 8cf22ee3f05c..0193d25704c5 100644
--- a/tools/opt-viewer/optrecord.py
+++ b/tools/opt-viewer/optrecord.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
from __future__ import print_function
diff --git a/tools/opt/Debugify.cpp b/tools/opt/Debugify.cpp
index 6c3cdc75e334..3b1effba1592 100644
--- a/tools/opt/Debugify.cpp
+++ b/tools/opt/Debugify.cpp
@@ -96,11 +96,12 @@ bool applyDebugifyMetadata(Module &M,
continue;
auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));
- bool IsLocalToUnit = F.hasPrivateLinkage() || F.hasInternalLinkage();
- auto SP =
- DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine, SPType,
- IsLocalToUnit, /*isDefinition=*/true, NextLine,
- DINode::FlagZero, /*isOptimized=*/true);
+ DISubprogram::DISPFlags SPFlags =
+ DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized;
+ if (F.hasPrivateLinkage() || F.hasInternalLinkage())
+ SPFlags |= DISubprogram::SPFlagLocalToUnit;
+ auto SP = DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine,
+ SPType, NextLine, DINode::FlagZero, SPFlags);
F.setSubprogram(SP);
for (BasicBlock &BB : F) {
// Attach debug locations.
diff --git a/tools/opt/NewPMDriver.cpp b/tools/opt/NewPMDriver.cpp
index a91d4cb5f9cd..211a3b151fe1 100644
--- a/tools/opt/NewPMDriver.cpp
+++ b/tools/opt/NewPMDriver.cpp
@@ -13,8 +13,8 @@
///
//===----------------------------------------------------------------------===//
-#include "Debugify.h"
#include "NewPMDriver.h"
+#include "Debugify.h"
#include "PassPrinters.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/AliasAnalysis.h"
@@ -29,6 +29,7 @@
#include "llvm/IR/Verifier.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
+#include "llvm/Passes/StandardInstrumentations.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ToolOutputFile.h"
@@ -94,6 +95,12 @@ static cl::opt<std::string> PipelineStartEPPipeline(
cl::desc("A textual description of the function pass pipeline inserted at "
"the PipelineStart extension point into default pipelines"),
cl::Hidden);
+static cl::opt<std::string> OptimizerLastEPPipeline(
+ "passes-ep-optimizer-last",
+ cl::desc("A textual description of the function pass pipeline inserted at "
+ "the OptimizerLast extension point into default pipelines"),
+ cl::Hidden);
+
enum PGOKind { NoPGO, InstrGen, InstrUse, SampleUse };
static cl::opt<PGOKind> PGOKindFlag(
"pgo-kind", cl::init(NoPGO), cl::Hidden,
@@ -107,24 +114,30 @@ static cl::opt<PGOKind> PGOKindFlag(
"Use sampled profile to guide PGO.")));
static cl::opt<std::string> ProfileFile(
"profile-file", cl::desc("Path to the profile."), cl::Hidden);
+static cl::opt<std::string>
+ ProfileRemappingFile("profile-remapping-file",
+ cl::desc("Path to the profile remapping file."),
+ cl::Hidden);
static cl::opt<bool> DebugInfoForProfiling(
"new-pm-debug-info-for-profiling", cl::init(false), cl::Hidden,
cl::desc("Emit special debug info to enable PGO profile generation."));
/// @}}
template <typename PassManagerT>
-bool tryParsePipelineText(PassBuilder &PB, StringRef PipelineText) {
- if (PipelineText.empty())
+bool tryParsePipelineText(PassBuilder &PB,
+ const cl::opt<std::string> &PipelineOpt) {
+ if (PipelineOpt.empty())
return false;
// Verify the pipeline is parseable:
PassManagerT PM;
- if (PB.parsePassPipeline(PM, PipelineText))
- return true;
-
- errs() << "Could not parse pipeline '" << PipelineText
- << "'. I'm going to igore it.\n";
- return false;
+ if (auto Err = PB.parsePassPipeline(PM, PipelineOpt)) {
+ errs() << "Could not parse -" << PipelineOpt.ArgStr
+ << " pipeline: " << toString(std::move(Err))
+ << "... I'm going to ignore it.\n";
+ return false;
+ }
+ return true;
}
/// If one of the EPPipeline command line options was given, register callbacks
@@ -132,50 +145,69 @@ bool tryParsePipelineText(PassBuilder &PB, StringRef PipelineText) {
static void registerEPCallbacks(PassBuilder &PB, bool VerifyEachPass,
bool DebugLogging) {
if (tryParsePipelineText<FunctionPassManager>(PB, PeepholeEPPipeline))
- PB.registerPeepholeEPCallback([&PB, VerifyEachPass, DebugLogging](
- FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
- PB.parsePassPipeline(PM, PeepholeEPPipeline, VerifyEachPass,
- DebugLogging);
- });
+ PB.registerPeepholeEPCallback(
+ [&PB, VerifyEachPass, DebugLogging](
+ FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
+ ExitOnError Err("Unable to parse PeepholeEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, PeepholeEPPipeline, VerifyEachPass,
+ DebugLogging));
+ });
if (tryParsePipelineText<LoopPassManager>(PB,
LateLoopOptimizationsEPPipeline))
PB.registerLateLoopOptimizationsEPCallback(
[&PB, VerifyEachPass, DebugLogging](
LoopPassManager &PM, PassBuilder::OptimizationLevel Level) {
- PB.parsePassPipeline(PM, LateLoopOptimizationsEPPipeline,
- VerifyEachPass, DebugLogging);
+ ExitOnError Err("Unable to parse LateLoopOptimizationsEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, LateLoopOptimizationsEPPipeline,
+ VerifyEachPass, DebugLogging));
});
if (tryParsePipelineText<LoopPassManager>(PB, LoopOptimizerEndEPPipeline))
- PB.registerLoopOptimizerEndEPCallback([&PB, VerifyEachPass, DebugLogging](
- LoopPassManager &PM, PassBuilder::OptimizationLevel Level) {
- PB.parsePassPipeline(PM, LoopOptimizerEndEPPipeline, VerifyEachPass,
- DebugLogging);
- });
+ PB.registerLoopOptimizerEndEPCallback(
+ [&PB, VerifyEachPass, DebugLogging](
+ LoopPassManager &PM, PassBuilder::OptimizationLevel Level) {
+ ExitOnError Err("Unable to parse LoopOptimizerEndEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, LoopOptimizerEndEPPipeline,
+ VerifyEachPass, DebugLogging));
+ });
if (tryParsePipelineText<FunctionPassManager>(PB,
ScalarOptimizerLateEPPipeline))
PB.registerScalarOptimizerLateEPCallback(
[&PB, VerifyEachPass, DebugLogging](
FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
- PB.parsePassPipeline(PM, ScalarOptimizerLateEPPipeline,
- VerifyEachPass, DebugLogging);
+ ExitOnError Err("Unable to parse ScalarOptimizerLateEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, ScalarOptimizerLateEPPipeline,
+ VerifyEachPass, DebugLogging));
});
if (tryParsePipelineText<CGSCCPassManager>(PB, CGSCCOptimizerLateEPPipeline))
- PB.registerCGSCCOptimizerLateEPCallback([&PB, VerifyEachPass, DebugLogging](
- CGSCCPassManager &PM, PassBuilder::OptimizationLevel Level) {
- PB.parsePassPipeline(PM, CGSCCOptimizerLateEPPipeline, VerifyEachPass,
- DebugLogging);
- });
+ PB.registerCGSCCOptimizerLateEPCallback(
+ [&PB, VerifyEachPass, DebugLogging](
+ CGSCCPassManager &PM, PassBuilder::OptimizationLevel Level) {
+ ExitOnError Err("Unable to parse CGSCCOptimizerLateEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, CGSCCOptimizerLateEPPipeline,
+ VerifyEachPass, DebugLogging));
+ });
if (tryParsePipelineText<FunctionPassManager>(PB, VectorizerStartEPPipeline))
- PB.registerVectorizerStartEPCallback([&PB, VerifyEachPass, DebugLogging](
- FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
- PB.parsePassPipeline(PM, VectorizerStartEPPipeline, VerifyEachPass,
- DebugLogging);
- });
+ PB.registerVectorizerStartEPCallback(
+ [&PB, VerifyEachPass, DebugLogging](
+ FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
+ ExitOnError Err("Unable to parse VectorizerStartEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, VectorizerStartEPPipeline,
+ VerifyEachPass, DebugLogging));
+ });
if (tryParsePipelineText<ModulePassManager>(PB, PipelineStartEPPipeline))
PB.registerPipelineStartEPCallback(
[&PB, VerifyEachPass, DebugLogging](ModulePassManager &PM) {
- PB.parsePassPipeline(PM, PipelineStartEPPipeline, VerifyEachPass,
- DebugLogging);
+ ExitOnError Err("Unable to parse PipelineStartEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, PipelineStartEPPipeline, VerifyEachPass,
+ DebugLogging));
+ });
+ if (tryParsePipelineText<FunctionPassManager>(PB, OptimizerLastEPPipeline))
+ PB.registerOptimizerLastEPCallback(
+ [&PB, VerifyEachPass, DebugLogging](FunctionPassManager &PM,
+ PassBuilder::OptimizationLevel) {
+ ExitOnError Err("Unable to parse OptimizerLastEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, OptimizerLastEPPipeline, VerifyEachPass,
+ DebugLogging));
});
}
@@ -199,21 +231,25 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
Optional<PGOOptions> P;
switch (PGOKindFlag) {
case InstrGen:
- P = PGOOptions(ProfileFile, "", "", true);
+ P = PGOOptions(ProfileFile, "", "", "", true);
break;
case InstrUse:
- P = PGOOptions("", ProfileFile, "", false);
+ P = PGOOptions("", ProfileFile, "", ProfileRemappingFile, false);
break;
case SampleUse:
- P = PGOOptions("", "", ProfileFile, false);
+ P = PGOOptions("", "", ProfileFile, ProfileRemappingFile, false);
break;
case NoPGO:
if (DebugInfoForProfiling)
- P = PGOOptions("", "", "", false, true);
+ P = PGOOptions("", "", "", "", false, true);
else
P = None;
}
- PassBuilder PB(TM, P);
+ PassInstrumentationCallbacks PIC;
+ StandardInstrumentations SI;
+ SI.registerCallbacks(PIC);
+
+ PassBuilder PB(TM, P, &PIC);
registerEPCallbacks(PB, VerifyEachPass, DebugPM);
// Load requested pass plugins and let them register pass builder callbacks
@@ -249,8 +285,8 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
// Specially handle the alias analysis manager so that we can register
// a custom pipeline of AA passes with it.
AAManager AA;
- if (!PB.parseAAPipeline(AA, AAPipeline)) {
- errs() << Arg0 << ": unable to parse AA pipeline description.\n";
+ if (auto Err = PB.parseAAPipeline(AA, AAPipeline)) {
+ errs() << Arg0 << ": " << toString(std::move(Err)) << "\n";
return false;
}
@@ -275,8 +311,9 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
if (EnableDebugify)
MPM.addPass(NewPMDebugifyPass());
- if (!PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) {
- errs() << Arg0 << ": unable to parse pass pipeline description.\n";
+ if (auto Err =
+ PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) {
+ errs() << Arg0 << ": " << toString(std::move(Err)) << "\n";
return false;
}
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 6e287b6c0ab6..a4967a234d9c 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -103,6 +103,10 @@ static cl::opt<bool>
OutputThinLTOBC("thinlto-bc",
cl::desc("Write output as ThinLTO-ready bitcode"));
+static cl::opt<bool>
+ SplitLTOUnit("thinlto-split-lto-unit",
+ cl::desc("Enable splitting of a ThinLTO LTOUnit"));
+
static cl::opt<std::string> ThinLinkBitcodeFile(
"thin-link-bitcode-file", cl::value_desc("filename"),
cl::desc(
@@ -463,6 +467,7 @@ int main(int argc, char **argv) {
initializePreISelIntrinsicLoweringLegacyPassPass(Registry);
initializeGlobalMergePass(Registry);
initializeIndirectBrExpandPassPass(Registry);
+ initializeInterleavedLoadCombinePass(Registry);
initializeInterleavedAccessPass(Registry);
initializeEntryExitInstrumenterPass(Registry);
initializePostInlineEntryExitInstrumenterPass(Registry);
@@ -595,6 +600,9 @@ int main(int argc, char **argv) {
if (CheckBitcodeOutputToConsole(Out->os(), !Quiet))
NoOutput = true;
+ if (OutputThinLTOBC)
+ M->addModuleFlag(Module::Error, "EnableSplitLTOUnit", SplitLTOUnit);
+
if (PassPipeline.getNumOccurrences() > 0) {
OutputKind OK = OK_NoOutput;
if (!NoOutput)
diff --git a/tools/sancov/coverage-report-server.py b/tools/sancov/coverage-report-server.py
index a2e161d0de58..2fb70eecfcfa 100755
--- a/tools/sancov/coverage-report-server.py
+++ b/tools/sancov/coverage-report-server.py
@@ -22,6 +22,8 @@ Other options:
--host host_name - host name to bind server to (127.0.0.1)
'''
+from __future__ import print_function
+
import argparse
import http.server
import json
diff --git a/tools/sancov/sancov.cpp b/tools/sancov/sancov.cpp
index 0bddd35a96f2..e8935d1be212 100644
--- a/tools/sancov/sancov.cpp
+++ b/tools/sancov/sancov.cpp
@@ -766,6 +766,19 @@ findSanitizerCovFunctions(const object::ObjectFile &O) {
return Result;
}
+static uint64_t getPreviousInstructionPc(uint64_t PC,
+ Triple TheTriple) {
+ if (TheTriple.isARM()) {
+ return (PC - 3) & (~1);
+ } else if (TheTriple.isAArch64()) {
+ return PC - 4;
+ } else if (TheTriple.isMIPS()) {
+ return PC - 8;
+ } else {
+ return PC - 1;
+ }
+}
+
// Locate addresses of all coverage points in a file. Coverage point
// is defined as the 'address of instruction following __sanitizer_cov
// call - 1'.
@@ -832,7 +845,7 @@ static void getObjectCoveragePoints(const object::ObjectFile &O,
}
uint64_t Addr = Index + SectionAddr;
// Sanitizer coverage uses the address of the next instruction - 1.
- uint64_t CovPoint = Addr + Size - 1;
+ uint64_t CovPoint = getPreviousInstructionPc(Addr + Size, TheTriple);
uint64_t Target;
if (MIA->isCall(Inst) &&
MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target) &&
diff --git a/tools/sanstats/sanstats.cpp b/tools/sanstats/sanstats.cpp
index 71f0207bab50..a345e9f06bed 100644
--- a/tools/sanstats/sanstats.cpp
+++ b/tools/sanstats/sanstats.cpp
@@ -15,7 +15,9 @@
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
#include "llvm/Transforms/Utils/SanitizerStats.h"
#include <stdint.h>
@@ -52,7 +54,11 @@ const char *ReadModule(char SizeofPtr, const char *Begin, const char *End) {
++Begin;
if (Begin == End)
return nullptr;
- StringRef Filename(FilenameBegin, Begin - FilenameBegin);
+ std::string Filename(FilenameBegin, Begin - FilenameBegin);
+
+ if (!llvm::sys::fs::exists(Filename))
+ Filename = std::string(llvm::sys::path::parent_path(ClInputFile)) +
+ std::string(llvm::sys::path::filename(Filename));
++Begin;
if (Begin == End)
@@ -81,8 +87,9 @@ const char *ReadModule(char SizeofPtr, const char *Begin, const char *End) {
// remove one from the address to get the correct DI.
if (Expected<DILineInfo> LineInfo =
Symbolizer.symbolizeCode(Filename, Addr - 1)) {
- llvm::outs() << LineInfo->FileName << ':' << LineInfo->Line << ' '
- << LineInfo->FunctionName << ' ';
+ llvm::outs() << format_hex(Addr - 1, 18) << ' ' << LineInfo->FileName
+ << ':' << LineInfo->Line << ' ' << LineInfo->FunctionName
+ << ' ';
} else {
logAllUnhandledErrors(LineInfo.takeError(), llvm::outs(), "<error> ");
}
diff --git a/tools/xcode-toolchain/CMakeLists.txt b/tools/xcode-toolchain/CMakeLists.txt
index 0ae5e374fe9f..6167f5f6bdd7 100644
--- a/tools/xcode-toolchain/CMakeLists.txt
+++ b/tools/xcode-toolchain/CMakeLists.txt
@@ -100,7 +100,7 @@ add_llvm_install_targets(install-xcode-toolchain
PREFIX ${LLVMToolchainDir}/usr/)
if(LLVM_DISTRIBUTION_COMPONENTS)
- if(CMAKE_CONFIGURATION_TYPES)
+ if(LLVM_ENABLE_IDE)
message(FATAL_ERROR "LLVM_DISTRIBUTION_COMPONENTS cannot be specified with multi-configuration generators (i.e. Xcode or Visual Studio)")
endif()
diff --git a/tools/yaml2obj/yaml2coff.cpp b/tools/yaml2obj/yaml2coff.cpp
index befa01b369c0..4fe9ab090414 100644
--- a/tools/yaml2obj/yaml2coff.cpp
+++ b/tools/yaml2obj/yaml2coff.cpp
@@ -24,6 +24,7 @@
#include "llvm/Support/Endian.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <vector>
@@ -46,7 +47,8 @@ struct COFFParser {
bool isPE() const { return Obj.OptionalHeader.hasValue(); }
bool is64Bit() const {
- return Obj.Header.Machine == COFF::IMAGE_FILE_MACHINE_AMD64;
+ return Obj.Header.Machine == COFF::IMAGE_FILE_MACHINE_AMD64 ||
+ Obj.Header.Machine == COFF::IMAGE_FILE_MACHINE_ARM64;
}
uint32_t getFileAlignment() const {
@@ -519,7 +521,15 @@ static bool writeCOFF(COFFParser &CP, raw_ostream &OS) {
assert(S.Header.SizeOfRawData >= S.SectionData.binary_size());
OS << num_zeros(S.Header.SizeOfRawData - S.SectionData.binary_size());
for (const COFFYAML::Relocation &R : S.Relocations) {
- uint32_t SymbolTableIndex = SymbolTableIndexMap[R.SymbolName];
+ uint32_t SymbolTableIndex;
+ if (R.SymbolTableIndex) {
+ if (!R.SymbolName.empty())
+ WithColor::error()
+ << "Both SymbolName and SymbolTableIndex specified\n";
+ SymbolTableIndex = *R.SymbolTableIndex;
+ } else {
+ SymbolTableIndex = SymbolTableIndexMap[R.SymbolName];
+ }
OS << binary_le(R.VirtualAddress)
<< binary_le(SymbolTableIndex)
<< binary_le(R.Type);
diff --git a/tools/yaml2obj/yaml2elf.cpp b/tools/yaml2obj/yaml2elf.cpp
index 7ca5ac206c9e..5e23bc6f0850 100644
--- a/tools/yaml2obj/yaml2elf.cpp
+++ b/tools/yaml2obj/yaml2elf.cpp
@@ -196,7 +196,7 @@ void ELFState<ELFT>::initELFHeader(Elf_Ehdr &Header) {
Header.e_ident[EI_DATA] = IsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB;
Header.e_ident[EI_VERSION] = EV_CURRENT;
Header.e_ident[EI_OSABI] = Doc.Header.OSABI;
- Header.e_ident[EI_ABIVERSION] = 0;
+ Header.e_ident[EI_ABIVERSION] = Doc.Header.ABIVersion;
Header.e_type = Doc.Header.Type;
Header.e_machine = Doc.Header.Machine;
Header.e_version = EV_CURRENT;
@@ -226,6 +226,16 @@ void ELFState<ELFT>::initProgramHeaders(std::vector<Elf_Phdr> &PHeaders) {
}
}
+static bool convertSectionIndex(NameToIdxMap &SN2I, StringRef SecName,
+ StringRef IndexSrc, unsigned &IndexDest) {
+ if (SN2I.lookup(IndexSrc, IndexDest) && !to_integer(IndexSrc, IndexDest)) {
+ WithColor::error() << "Unknown section referenced: '" << IndexSrc
+ << "' at YAML section '" << SecName << "'.\n";
+ return false;
+ }
+ return true;
+}
+
template <class ELFT>
bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
ContiguousBlobAccumulator &CBA) {
@@ -245,11 +255,8 @@ bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
if (!Sec->Link.empty()) {
unsigned Index;
- if (SN2I.lookup(Sec->Link, Index)) {
- WithColor::error() << "Unknown section referenced: '" << Sec->Link
- << "' at YAML section '" << Sec->Name << "'.\n";
+ if (!convertSectionIndex(SN2I, Sec->Name, Sec->Link, Index))
return false;
- }
SHeader.sh_link = Index;
}
@@ -261,20 +268,14 @@ bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
SHeader.sh_link = getDotSymTabSecNo();
unsigned Index;
- if (SN2I.lookup(S->Info, Index)) {
- if (S->Info.getAsInteger(0, Index)) {
- WithColor::error() << "Unknown section referenced: '" << S->Info
- << "' at YAML section '" << S->Name << "'.\n";
- return false;
- }
- }
+ if (!convertSectionIndex(SN2I, S->Name, S->Info, Index))
+ return false;
SHeader.sh_info = Index;
-
if (!writeSectionContent(SHeader, *S, CBA))
return false;
} else if (auto S = dyn_cast<ELFYAML::Group>(Sec.get())) {
unsigned SymIdx;
- if (SymN2I.lookup(S->Info, SymIdx)) {
+ if (SymN2I.lookup(S->Info, SymIdx) && !to_integer(S->Info, SymIdx)) {
WithColor::error() << "Unknown symbol referenced: '" << S->Info
<< "' at YAML section '" << S->Name << "'.\n";
return false;
@@ -461,7 +462,9 @@ ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader,
Section.Content.writeAsBinary(OS);
for (auto i = Section.Content.binary_size(); i < Section.Size; ++i)
OS.write(0);
- if (Section.Type == llvm::ELF::SHT_RELR)
+ if (Section.EntSize)
+ SHeader.sh_entsize = *Section.EntSize;
+ else if (Section.Type == llvm::ELF::SHT_RELR)
SHeader.sh_entsize = sizeof(Elf_Relr);
else if (Section.Type == llvm::ELF::SHT_DYNAMIC)
SHeader.sh_entsize = sizeof(Elf_Dyn);
@@ -535,12 +538,9 @@ bool ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader,
unsigned int sectionIndex = 0;
if (member.sectionNameOrType == "GRP_COMDAT")
sectionIndex = llvm::ELF::GRP_COMDAT;
- else if (SN2I.lookup(member.sectionNameOrType, sectionIndex)) {
- WithColor::error() << "Unknown section referenced: '"
- << member.sectionNameOrType << "' at YAML section' "
- << Section.Name << "\n";
+ else if (!convertSectionIndex(SN2I, Section.Name, member.sectionNameOrType,
+ sectionIndex))
return false;
- }
SIdx = sectionIndex;
OS.write((const char *)&SIdx, sizeof(SIdx));
}
@@ -685,7 +685,8 @@ template <class ELFT> bool ELFState<ELFT>::hasDynamicSymbols() const {
Doc.DynamicSymbols.Local.size() > 0;
}
-template <class ELFT> SmallVector<const char *, 5> ELFState<ELFT>::implicitSectionNames() const {
+template <class ELFT>
+SmallVector<const char *, 5> ELFState<ELFT>::implicitSectionNames() const {
if (!hasDynamicSymbols())
return {".symtab", ".strtab", ".shstrtab"};
return {".symtab", ".strtab", ".shstrtab", ".dynsym", ".dynstr"};
diff --git a/tools/yaml2obj/yaml2macho.cpp b/tools/yaml2obj/yaml2macho.cpp
index 23fe946f1da3..1f36431c05de 100644
--- a/tools/yaml2obj/yaml2macho.cpp
+++ b/tools/yaml2obj/yaml2macho.cpp
@@ -417,10 +417,9 @@ Error MachOWriter::writeLinkEditData(raw_ostream &OS) {
}
}
- llvm::sort(WriteQueue.begin(), WriteQueue.end(),
- [](const writeOperation &a, const writeOperation &b) {
- return a.first < b.first;
- });
+ llvm::sort(WriteQueue, [](const writeOperation &a, const writeOperation &b) {
+ return a.first < b.first;
+ });
for (auto writeOp : WriteQueue) {
ZeroToOffset(OS, writeOp.first);
diff --git a/tools/yaml2obj/yaml2wasm.cpp b/tools/yaml2obj/yaml2wasm.cpp
index a7ec25e31c73..2d3e3b71f086 100644
--- a/tools/yaml2obj/yaml2wasm.cpp
+++ b/tools/yaml2obj/yaml2wasm.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
//
+#include "llvm/Object/Wasm.h"
#include "llvm/ObjectYAML/ObjectYAML.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/LEB128.h"
@@ -37,6 +38,7 @@ private:
int writeSectionContent(raw_ostream &OS, WasmYAML::TableSection &Section);
int writeSectionContent(raw_ostream &OS, WasmYAML::MemorySection &Section);
int writeSectionContent(raw_ostream &OS, WasmYAML::GlobalSection &Section);
+ int writeSectionContent(raw_ostream &OS, WasmYAML::EventSection &Section);
int writeSectionContent(raw_ostream &OS, WasmYAML::ExportSection &Section);
int writeSectionContent(raw_ostream &OS, WasmYAML::StartSection &Section);
int writeSectionContent(raw_ostream &OS, WasmYAML::ElemSection &Section);
@@ -44,11 +46,13 @@ private:
int writeSectionContent(raw_ostream &OS, WasmYAML::DataSection &Section);
// Custom section types
+ int writeSectionContent(raw_ostream &OS, WasmYAML::DylinkSection &Section);
int writeSectionContent(raw_ostream &OS, WasmYAML::NameSection &Section);
int writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &Section);
WasmYAML::Object &Obj;
uint32_t NumImportedFunctions = 0;
uint32_t NumImportedGlobals = 0;
+ uint32_t NumImportedEvents = 0;
};
static int writeUint64(raw_ostream &OS, uint64_t Value) {
@@ -101,7 +105,7 @@ static int writeInitExpr(const wasm::WasmInitExpr &InitExpr, raw_ostream &OS) {
case wasm::WASM_OPCODE_F64_CONST:
writeUint64(OS, InitExpr.Value.Float64);
break;
- case wasm::WASM_OPCODE_GET_GLOBAL:
+ case wasm::WASM_OPCODE_GLOBAL_GET:
encodeULEB128(InitExpr.Value.Global, OS);
break;
default:
@@ -127,12 +131,25 @@ public:
OutString.clear();
}
- raw_ostream& GetStream() {
- return StringStream;
- }
+ raw_ostream &GetStream() { return StringStream; }
};
-int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &Section) {
+int WasmWriter::writeSectionContent(raw_ostream &OS,
+ WasmYAML::DylinkSection &Section) {
+ writeStringRef(Section.Name, OS);
+ encodeULEB128(Section.MemorySize, OS);
+ encodeULEB128(Section.MemoryAlignment, OS);
+ encodeULEB128(Section.TableSize, OS);
+ encodeULEB128(Section.TableAlignment, OS);
+ encodeULEB128(Section.Needed.size(), OS);
+ for (StringRef Needed : Section.Needed) {
+ writeStringRef(Needed, OS);
+ }
+ return 0;
+}
+
+int WasmWriter::writeSectionContent(raw_ostream &OS,
+ WasmYAML::LinkingSection &Section) {
writeStringRef(Section.Name, OS);
encodeULEB128(Section.Version, OS);
@@ -153,6 +170,7 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &S
switch (Info.Kind) {
case wasm::WASM_SYMBOL_TYPE_FUNCTION:
case wasm::WASM_SYMBOL_TYPE_GLOBAL:
+ case wasm::WASM_SYMBOL_TYPE_EVENT:
encodeULEB128(Info.ElementIndex, SubSection.GetStream());
if ((Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0)
writeStringRef(Info.Name, SubSection.GetStream());
@@ -218,7 +236,8 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &S
return 0;
}
-int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::NameSection &Section) {
+int WasmWriter::writeSectionContent(raw_ostream &OS,
+ WasmYAML::NameSection &Section) {
writeStringRef(Section.Name, OS);
if (Section.FunctionNames.size()) {
writeUint8(OS, wasm::WASM_NAMES_FUNCTION);
@@ -238,7 +257,10 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::NameSection &Sect
int WasmWriter::writeSectionContent(raw_ostream &OS,
WasmYAML::CustomSection &Section) {
- if (auto S = dyn_cast<WasmYAML::NameSection>(&Section)) {
+ if (auto S = dyn_cast<WasmYAML::DylinkSection>(&Section)) {
+ if (auto Err = writeSectionContent(OS, *S))
+ return Err;
+ } else if (auto S = dyn_cast<WasmYAML::NameSection>(&Section)) {
if (auto Err = writeSectionContent(OS, *S))
return Err;
} else if (auto S = dyn_cast<WasmYAML::LinkingSection>(&Section)) {
@@ -292,11 +314,16 @@ int WasmWriter::writeSectionContent(raw_ostream &OS,
writeUint8(OS, Import.GlobalImport.Mutable);
NumImportedGlobals++;
break;
+ case wasm::WASM_EXTERNAL_EVENT:
+ writeUint32(OS, Import.EventImport.Attribute);
+ writeUint32(OS, Import.EventImport.SigIndex);
+ NumImportedGlobals++;
+ break;
case wasm::WASM_EXTERNAL_MEMORY:
writeLimits(Import.Memory, OS);
break;
case wasm::WASM_EXTERNAL_TABLE:
- writeUint8(OS,Import.TableImport.ElemType);
+ writeUint8(OS, Import.TableImport.ElemType);
writeLimits(Import.TableImport.TableLimits, OS);
break;
default:
@@ -370,6 +397,22 @@ int WasmWriter::writeSectionContent(raw_ostream &OS,
}
int WasmWriter::writeSectionContent(raw_ostream &OS,
+ WasmYAML::EventSection &Section) {
+ encodeULEB128(Section.Events.size(), OS);
+ uint32_t ExpectedIndex = NumImportedEvents;
+ for (auto &Event : Section.Events) {
+ if (Event.Index != ExpectedIndex) {
+ errs() << "Unexpected event index: " << Event.Index << "\n";
+ return 1;
+ }
+ ++ExpectedIndex;
+ encodeULEB128(Event.Attribute, OS);
+ encodeULEB128(Event.SigIndex, OS);
+ }
+ return 0;
+}
+
+int WasmWriter::writeSectionContent(raw_ostream &OS,
WasmYAML::ElemSection &Section) {
encodeULEB128(Section.Segments.size(), OS);
for (auto &Segment : Section.Segments) {
@@ -428,64 +471,61 @@ int WasmWriter::writeSectionContent(raw_ostream &OS,
int WasmWriter::writeRelocSection(raw_ostream &OS, WasmYAML::Section &Sec,
uint32_t SectionIndex) {
switch (Sec.Type) {
- case wasm::WASM_SEC_CODE:
- writeStringRef("reloc.CODE", OS);
- break;
- case wasm::WASM_SEC_DATA:
- writeStringRef("reloc.DATA", OS);
- break;
- case wasm::WASM_SEC_CUSTOM: {
- auto CustomSection = dyn_cast<WasmYAML::CustomSection>(&Sec);
- if (!CustomSection->Name.startswith(".debug_")) {
- llvm_unreachable("not yet implemented (only for debug sections)");
- return 1;
- }
-
- writeStringRef(("reloc." + CustomSection->Name).str(), OS);
- break;
- }
- default:
- llvm_unreachable("not yet implemented");
+ case wasm::WASM_SEC_CODE:
+ writeStringRef("reloc.CODE", OS);
+ break;
+ case wasm::WASM_SEC_DATA:
+ writeStringRef("reloc.DATA", OS);
+ break;
+ case wasm::WASM_SEC_CUSTOM: {
+ auto CustomSection = dyn_cast<WasmYAML::CustomSection>(&Sec);
+ if (!CustomSection->Name.startswith(".debug_")) {
+ llvm_unreachable("not yet implemented (only for debug sections)");
return 1;
+ }
+
+ writeStringRef(("reloc." + CustomSection->Name).str(), OS);
+ break;
+ }
+ default:
+ llvm_unreachable("not yet implemented");
+ return 1;
}
encodeULEB128(SectionIndex, OS);
encodeULEB128(Sec.Relocations.size(), OS);
- for (auto Reloc: Sec.Relocations) {
+ for (auto Reloc : Sec.Relocations) {
writeUint8(OS, Reloc.Type);
encodeULEB128(Reloc.Offset, OS);
encodeULEB128(Reloc.Index, OS);
switch (Reloc.Type) {
- case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB:
- case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
- case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32:
- case wasm::R_WEBASSEMBLY_FUNCTION_OFFSET_I32:
- case wasm::R_WEBASSEMBLY_SECTION_OFFSET_I32:
- encodeULEB128(Reloc.Addend, OS);
+ case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB:
+ case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+ case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ case wasm::R_WEBASSEMBLY_FUNCTION_OFFSET_I32:
+ case wasm::R_WEBASSEMBLY_SECTION_OFFSET_I32:
+ encodeULEB128(Reloc.Addend, OS);
}
}
return 0;
}
-
int WasmWriter::writeWasm(raw_ostream &OS) {
// Write headers
OS.write(wasm::WasmMagic, sizeof(wasm::WasmMagic));
writeUint32(OS, Obj.Header.Version);
// Write each section
- uint32_t LastType = 0;
+ llvm::object::WasmSectionOrderChecker Checker;
for (const std::unique_ptr<WasmYAML::Section> &Sec : Obj.Sections) {
- uint32_t Type = Sec->Type;
- if (Type != wasm::WASM_SEC_CUSTOM) {
- if (Type < LastType) {
- errs() << "Out of order section type: " << Type << "\n";
- return 1;
- }
- LastType = Type;
+ StringRef SecName = "";
+ if (auto S = dyn_cast<WasmYAML::CustomSection>(Sec.get()))
+ SecName = S->Name;
+ if (!Checker.isValidSectionOrder(Sec->Type, SecName)) {
+ errs() << "Out of order section type: " << Sec->Type << "\n";
+ return 1;
}
-
encodeULEB128(Sec->Type, OS);
std::string OutString;
raw_string_ostream StringStream(OutString);
@@ -510,6 +550,9 @@ int WasmWriter::writeWasm(raw_ostream &OS) {
} else if (auto S = dyn_cast<WasmYAML::GlobalSection>(Sec.get())) {
if (auto Err = writeSectionContent(StringStream, *S))
return Err;
+ } else if (auto S = dyn_cast<WasmYAML::EventSection>(Sec.get())) {
+ if (auto Err = writeSectionContent(StringStream, *S))
+ return Err;
} else if (auto S = dyn_cast<WasmYAML::ExportSection>(Sec.get())) {
if (auto Err = writeSectionContent(StringStream, *S))
return Err;